I have CSV file like
Title|Column|Value
A|Z1|1
A|Z1|2
A|Z1|3
A|Z2|1
A|Z2|5
B|Z3|4
B|Z3|6
....
I want to read this csv file into the following class hierarchy:
(I want to end up with a List of MyClass)
class MyClass
{
public string Title { get; set; }
public List<Column> Columns { get; set; }
}
class Column
{
public string Column { get; set; }
public List<Value> Values { get; set; }
}
class Value
{
public string Value { get; set; }
}
I am not able to comprehend how to achieve this.
What I tried up till now is:
class DTO
{
public string Title { get; set; }
public string Column { get; set; }
public string Value { get; set; }
}
List<DTO> records = new List<DTO>();
using (var reader = new StreamReader(model.Path))
using (var csv = new CsvReader(reader))
{
records = csv.GetRecords<DTO>().ToList();
}
var temp = records
.GroupBy(x => x.Title)
.Select(y => new
{
Title = y.Key,
Columns = y.SelectMany(x => new { x.Column, x.Value }) //I am not sure how to proceed from here
});
The issue I am facing is how can I further group by on the columns and get the values, or if I should be using another approach?
Some more explanation on the csv structure:
There can be multiple Values for each column, and there can be multiple columns for each Title.
There can be multiple Values for each column, and there can be multiple columns for each Title.
That means that you have to repeat the first step and GroupBy again by columns. You also should use your types that you already implemented for this. Don't use anonymous types:
IEnumerable<MyClass> temp = records
.GroupBy(x => x.Title)
.Select(y => new MyClass
{
Title = y.Key,
Columns = y.GroupBy(x => x.Column)
.Select( c => new Column
{
Column_ = c.Key,
Values = c.Select(v => new Value
{
Value_ = v.Value
}).ToList()
}).ToList()
});
temp.Dump();
Result (from the LINQPad Dump):
PS. I changed the names of the properties, because the compiler does not allow them to be named exactly as the class is.
class Column
{
public string Column_ { get; set; }
public List<Value> Values { get; set; }
}
class Value
{
public string Value_ { get; set; }
}
Related
This query gets all the rows from the join table, TagRecipes (many-to-many), where the TagId is found in a list, tagIdList, and lastly just returns the Recipe. How can I make it so it only returns Recipes that have all the tags in the list, tagIdList?
Basically, it's filtering by 'or', but I want it to filter by 'and'. The recipe must contain ALL the tags, not just some.
var allRecipes = await _context.TagRecipes
.Where(tr => tagIdList.Contains(tr.TagId))
.Select(i => i.Recipe).Distinct()
.ToListAsync();
e.g, tagIdList = {17, 20 ,21 }
TagRecipes
So, it should only return Recipe with RecipeId = 2, even though RecipeID 4 contains TagId 17
Classes
public class Recipe
{
public int Id { get; set; }
public string Name { get; set; }
public string Ingredients { get; set; }
public string Instructions { get; set; }
public string ImageURL { get; set; }
public string Description { get; set; }
public ICollection<TagRecipe> TagRecipes { get; set; }
public virtual ICollection<StarRating> StarRatings { get; set; }
public ICollection<Binder> Binders { get; set; }
}
public class TagRecipe
{
public int TagId { get; set; }
public int RecipeId { get; set; }
public Tag Tag { get; set; }
public Recipe Recipe { get; set; }
}
Thank you
group TagRecipes by RecipId, so each RecipId (Key) has its own tagIds.
loop on each group to check if it has all the tags in the provided tagIdList, and if it has them all, store this RecipId (Key), in my case i created list of int RecipIds.
get all the Recipes in the RecipIds list.
I hope this could be helpful
List<int> RecipIds = new List<int>();
int count = 0;
var RecipGroup = _context.TagRecipes.GroupBy(tr => tr.RecipeId);
foreach (var group in RecipGroup)
{
count = 0;
foreach (var tr in group)
{
if (tagIdList.Contains(tr.TagId))
{
count += 1;
}
}
if (tagIdList.Length == count)
{
RecipIds.Add(group.Key);
}
}
var allRecipes = _context.Recipes.Where(r => RecipIds.Contains(r.Id)).ToList();
I believe the following solution using Linqkit will be the simplest way to solve this, and without returning duplicates.
var tagIdList = new List<int> {1, 2};
var predicate = tagIdList.Aggregate(PredicateBuilder.New<Recipe>(), (pred, currentTagId) =>
pred.And(recipe => recipe.TagRecipes.Any(x => x.TagId == currentTagId)));
var result = _context.Recipes.Where(predicate).ToList();
Generates this SQL:
SELECT "r"."Id", "r"."Name"
FROM "Recipes" AS "r"
WHERE EXISTS (
SELECT 1
FROM "TagRecipes" AS "t"
WHERE ("r"."Id" = "t"."RecipeId") AND ("t"."TagId" = #__currentTagId_0)) AND EXISTS (
SELECT 1
FROM "TagRecipes" AS "t0"
WHERE ("r"."Id" = "t0"."RecipeId") AND ("t0"."TagId" = #__currentTagId_1))
Code is tested and verified using an asp.net Core 5 app.
Basically, it's filtering by 'or', but I want it to filter by 'and'.
The recipe must contain ALL the tags, not just some.
So, it should only return Recipe with RecipeId = 2, even though RecipeID 4 contains
TagId 17
Since you want to filter the data by and, after filtering the data based on the tag, if group the result based on the RecipeId property, the item count in the group should be the equally with the tag list count. So, you could use the following query statement:
var tagIdList = new List<int>() { 17, 20, 21 };
var allRecipes = _context.TagRecipes.Include(tr => tr.Recipe)
.Where(tr => tagIdList.Contains(tr.TagId))
.ToList()
.GroupBy(tr=> tr.RecipeId)
.Where(c => c.Count() == tagIdList.Count)
.Select(i => new { RecipeId = i.Key, Count = i.Count(), Recipe = i.FirstOrDefault().Recipe })
.ToList();
The result as below:
I have these two classes:
public class Order
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOf { get; set; }
public int[] Product { get; set; }
public int[] Quantity { get; set; }
public int[] Storage { get; set; }
public override bool Equals(Order obj)
{
// If parameter is null return false.
if (obj == null)
{
return false;
}
// Return true if the fields match:
return (ID == obj.ID);
}
}
public class RawOrderData
{
public int ID { get; set; }
public int Output { get; set; }
public int Wharf { get; set; }
public int PartOfID { get; set; }
public int ProductID { get; set; }
public int Quantity { get; set; }
}
Every order in the system is in the form as class Order, the array is used when there are more than one product in the order.
RawOrderData is created from a JSON string where every product in the order have its own object. I want to create a List<Order> where every order gets its own object in the list so there not are several orders with same order id when order contains more than one product.
// raw data is here the JSON string
rawdatalist = serializer.Deserialize<List<RawOrderData>> (rawdata);
// Convert raw objects to List<Order>, list of orders
List<Order> orders = new List<Order> ();
orders = ConvertRawOrderToList (rawdatalist);
private List<Order> ConvertRawOrderToList(List<RawOrderData> datalist)
{
List<Order> orders = new List<Order> ();
foreach (RawOrderData dataobj in datalist)
{
// Check if order exists in list
if (orders.Contains(new Order () {ID = dataobj.ID}))
{
// Order exists, add more products
// CODE HERE?
} else {
// order not existing, add new order to list
short storage = GetStorageArea(dataobj.ProductID);
orders.Add (new Order () {ID = dataobj.ID, Output = dataobj.Output, Wharf = dataobj.Wharf, PartOf = dataobj.PartOfID, Product = dataobj.ProductID, Quantity = dataobj.Quantity});
}
}
return orders;
}
Do I think correct with the ConvertRawOrderToList method? The problem is I don't know what to write in // CODE HERE?. When there is array inside the list-object I'm confused.
I'm also wondering how to access all values in the List<Order> orders.
The information to Storage[] is created from another method that have product ID as input.
It sounds like you have a "flattened" collection of objects that you want to group into Orders. If that's the case, a basic Linq projection would be simplest:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
})
.ToList();
Then you don't need to worry about overriding Equals (at least not for this purpose).
Where would I add the method for adding Storage also?
Since your GetStorageArea function takes a single ProductID you need to pass the product IDs to that function:
var orders = datalist.GroupBy(o => o.ID)
.Select(g => new Order {
ID = g.Key,
Output = g.First().Output,
Wharf = g.First().Wharf,
PartOf = g.First().PartOf,
Product = g.Select(o => o.Product).ToArray(),
Quantity = g.Select(o => o.Product).ToArray(),
Storage = g.Select(o => GetStorageArea(o.Product)).ToArray()
})
.ToList();
I would like to select a where statement that adds items to a list where only product codes match. I have it so it gets all of the products sold in the sale but I would like there were statement to get only products in this sale.
PS: This is really hard to explain
Model
public class userSales
{
public string Name { get; set; }
public string UserName { get; set; }
public int Sale_Id { get; set; }
public int CostumerID { get; set; }
public string Sale_Date { get; set; }
public string Paid { get; set; }
public Nullable<int> Sale_Cost { get; set; }
public string Discount_Code { get; set; }
public List<SaleProduct> saleProductsList { get; set; }
}
public class SaleProduct
{
public int SaleID { get; set; }
public string ProductCode { get; set; }
public int ProductCount { get; set; }
public string Image_Path { get; set; }
public string Shoot_Date { get; set; }
public string Shoot_Info { get; set; }
}
Linq statement where I'm having trouble:
var test = (from _ClientData in db.ClientDatas
join _salesInfo in db.Sales_Infoes
on _ClientData.CostumerID
equals _salesInfo.CostumerID
where _ClientData.UserName == _userName
select new userSales()
{
CostumerID = _ClientData.CostumerID,
Name = _ClientData.Name,
UserName = _ClientData.UserName,
Sale_Id = _salesInfo.Sale_Id, // This is the item i would like to use in my were statement
Sale_Date = _salesInfo.Sale_Date,
Sale_Cost = _salesInfo.Sale_Cost,
Discount_Code = _salesInfo.Discount_Code,
Paid = _salesInfo.Paid,
// Problem here
saleProductsList = db.SaleProducts.Where()
}).ToList();
Got to this based on the answer:
var reult = db.ClientDatas.Where(a => a.UserName == _userName)
.Join(db.Sales_Infoes,
a => a.CostumerID,
b => b.CostumerID,
(a, b) => new userSales()
{
CostumerID = a.CostumerID,
Discount_Code = b.Discount_Code,
Sale_Cost = b.Sale_Cost,
Sale_Id= b.Sale_Id,
Name = a.Name,
Sale_Date = b.Sale_Date,
UserName = a.UserName,
Paid = b.Paid,
saleProductsList = db.SaleProducts.Where(c => c.SaleID == b.Sale_Id).ToList()
}).ToList();
You're not looking for a where, you're looking for a join. Where filters the results on a single table, join intersects two tables which is actually what you want here.
var result = db.Sales_Infoes.Where(x => x.UserName == _userName)
.Join(db.ClientDatas,
x => x.Sale_Id,
y => y.Sale_id,
(x, y) => new userSales() {
// x is SalesInfo obj y is ClientDatas obj do assignement here
Name = y.Name,
Sale_Date = y.Sale_date
}).ToList();
Just fyi I haven't had a chance to test that but it's the basic idea. You don't need a select like in your statement because the last argument I'm passing into join is the lambda (x, y) => ... in that case x and y are the current row from each table (that we've gotten from applying our where to the user sales table then joining those results into the salesproduct table) so whatever projections you want to do occur there. The other two method args above that are the telling join which fields to compare, it's the 'key selector' lambda expression for each table.
I have two classes MoviesListRootObject and Response. MoviesListRootObject conatins list of Response I want to access the id, Title and description that comes to List<Response> and assign that to var.
public class MoviesListRootObject
{
public int count { get; set; }
public Pagination pagination { get; set; }
public List<Response> response { get; set; }
}
[Serializable]
public class Response
{
public int id { get; set; }
public string title { get; set; }
public string title_language { get; set; }
public string description { get; set; }
public string description_language { get; set; }
}
Now What I can think of writing LINQ to get MovieDetails object containing reponse but that wont help.
var movieResponse = from MoviesListRootObject movieDetail in rootObj
select new MovieDetails
{
Response =movieDetail.response
};
It's not really clear what you are trying to achieve. My answer assumes that you simply want to have the properties of all Responses in all MoviesListRootObject:
var result = rootObj.SelectMany(x => x.response)
.Select(x => new { x.id, x.title, x.description });
You don't even need an anonymous class here:
var result = rootObj.SelectMany(x => x.response);
// result will be of type IEnumerable<Response>
var movieResponse = rootObj
.SelectMany(m => m.response)
.Select(m => new {
id = m.id, // or just m.id, as name is the same
title = m.title, //idem
description = m.description //idem
});
Are you asking about SelectMany?
var allResponses = rootObj.SelectMany(d => d.Response);
Will give you all the Responses for all movies in rootObj.
if rootObj is in fact an instance of MoviesListRootObject you don't need SelectMany,
var responses = rootObj.response;
will do.
i have an act:
public class Act
{
public Act()
{
this.Tags = new List<Tag>();
}
public string Id {get; set;}
public string Name { get; set; }
public IList<Tag> Tags { get; set; }
}
Tag is just:
public class Tag
{
public string Name { get; set; }
public string LfmUrl { get; set; }
}
I want to be able to query the DB and get a list of TagCount back which shows the name of the tag and the number of times it appears.
So far I have this:
public class Tags_ByTagCloud : AbstractIndexCreationTask
{
public override IndexDefinition CreateIndexDefinition()
{
return new IndexDefinition<Act, TagAndCount>
{
Map = docs => from doc in docs
from tag in doc.Tags
select new
{
Name = tag.Name,
Count = 1
},
Reduce = results => from result in results
group result by result.TagName
into g
select new
{
Name = g.Key,
Count = g.Sum(x => x.Count)
},
SortOptions = {{x => x.Count, SortOptions.Int}}
}.ToIndexDefinition(DocumentStore.Conventions);
}
}
Which happily outputs nothing. I know there are thousands of acts each with at least a few tags. Any ideas?
I ideally want to pass in a list of act ids to query this against. So, given a list of acts, what are the tags and how many times does
each occur?
Just had to change all TagName to match Name and it mapped nicely