I have the following classes:
public class Item
{
public int Id { get; set; }
public string Sku { get; set; }
public List<Price> Prices { get; set; }
}
public class Price
{
public int Id { get; set; }
public double Cost { get; set; }
public PriceList List { get; set; }
}
public class PriceList
{
public int Id { get; set; }
public string FileName { get; set; }
public DateTime ImportDateTime { get; set; }
}
Essentially, it's for a list of items [ Item
] in which each item has a list of prices [ List<Price> Prices
] (one-to-many), and each price has one price list [ PriceList List
] (one-to-one).
What I need is a list of all price lists.
It seems like what I need is a group to a "grandchild", basically, grouping by the PriceList
's Id
(which is under Price
, which in turn is under Item
in the list).
So, as an example, if I have five price lists, it should return five rows.
I achieved it doing the following long way:
List<PriceList> priceLists = new List<PriceList>();
foreach (Item item in items)
{
foreach (Price price in item.Prices)
{
PriceList list = price.List;
if (!priceLists.Any(x => x.Id == list.Id))
{
priceLists.Add(list);
}
}
}
How can it be achieved using LINQ?
UPDATE:
Here's a basic sample set:
PriceList priceList1 = new PriceList { Id = 1, FileName = "Price List 1.csv" };
PriceList priceList2 = new PriceList { Id = 2, FileName = "Price List 2.csv" };
Price price1 = new Price { Id = 1, Cost = 2.65, List = priceList1 };
Price price2 = new Price { Id = 2, Cost = 14.23, List = priceList2 };
Price price3 = new Price { Id = 3, Cost = 29.01, List = priceList1 };
Price price4 = new Price { Id = 4, Cost = 1, List = priceList2 };
Price price5 = new Price { Id = 5, Cost = 56.12, List = priceList1 };
Item item1 = new Item { Id = 1, Sku = "item1", Prices = new List<Price> { price1, price2 } };
Item item2 = new Item { Id = 2, Sku = "item2", Prices = new List<Price> { price3, price4 } };
Item item3 = new Item { Id = 3, Sku = "item3", Prices = new List<Price> { price5 } };
List<Item> itemsList = new List<Item> { item1, item2, item3 };
Let's start from the bottom upwards:
Item
( itemsList
), which contains three Item
s.Item
has a list of Price
.Price
has one PriceList
. To clarify the reasoning behind all this: The user imports a spreadsheet of prices (price list) they get periodically from their supplier, prices fluctuate and each price list has different prices for the same items. Hence the reason of having an item with many prices and each price has its price list which was used to upload it (there's more information included in the PriceList
class which I omitted to keep it simple).
I hope I'm clear.
This seems to be what you want, using an extension to do Distinct by a lambda expression:
var ans = itemsList.SelectMany(item => item.Prices.Select(price => price.List)).DistinctBy(price => price.Id);
The extension is as follows:
public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> src, Func<T, TKey> keyFun) {
var seenKeys = new HashSet<TKey>();
foreach (T e in src)
if (seenKeys.Add(keyFun(e)))
yield return e;
}
If you changed your approach to use a HashSet<Int>
to track the seen ids you would probably be a tiny bit more efficient than LINQ.
It looks like you can combine a SelectMany
(to get all the Prices
lists) with a Select
(to get all the associated PriceList
properties):
List<PriceList> priceLists = items.SelectMany(i => i.Prices).Select(p => p.List).ToList();
Edit
The above will return all the PriceLists
, but since many Items
could reference the same PriceList
, then you need a way to filter on the PriceList.Id
field.
One way to do that would be to override the Equals
and GetHashCode
property of the PriceList
object. If you truly consider two PriceList
objects to be equal if they have the same Id
, then you could do the following:
public class PriceList
{
public int Id { get; set; }
public string FileName { get; set; }
public DateTime ImportDateTime { get; set; }
public override bool Equals(object obj)
{
var other = obj as PriceList;
return Id == other?.Id;
}
public override int GetHashCode()
{
return Id;
}
}
This allows you to use the Linq
method, Distinct
on your prices (it will call the Equals
method to distinguish between PriceLists
in the results:
List<PriceList> priceLists = items
.SelectMany(i => i.Prices)
.Select(i => i.List)
.Distinct()
.ToList();
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.