I am currently trying to get an object deeply nested within a dictionaries and lists.
The object is the ProductPrice object within the ProductInfo dictionaries.
The structure is:
Debug info
I have tried the following LINQ however this only returns me the list (right before the object)
var Lis = productInfo
.Where(x => x.Key == "Prices")
.ToDictionary(x => x.Key, x => x.Value)
.Where(x => x.Key == "Prices")
.Select(x => x.Value)
.ToList()
.First();
The productinfo class is a dictionary which is of the type <string, object>. I dont know why the first() command still gives me the full list and not the object itself... any ideas of how to iterate the first dictionary find the prices dictionary then iterate that one. Retrieve the list and finally get the first element of it?
The result of my linq is her:
Linq variable first()
Update Product Info Class:
using System.Collections.Generic;
namespace Dynamicweb.Ecommerce.Integration
{
public class ProductInfo : Dictionary<string, object>
{
public ProductInfo();
}
}
You don't need to iterate anything. Your productInfo is a dictionary of objects. You need to cast the value of the dictionary access to the appropriate type (List<ProductPrice>) and operate on that. In this case, you can call First() to get the first item in the list.
var query = ((List<ProductPrice>)productInfo["Prices"]).First();
If it's possible to not have "Prices` in the dictionary or it's not in an expected type or potentially empty, you can check for that.
var query = productInfo.TryGetValue("Prices", out var v) && v is IEnumerable<ProductPrice> e
? e.FirstOrDefault()
: default;
Hard to be sure and I cannot test this becuase I dont have you classes or data so I can only go off your debug screen shots
var prices = productInfo["Prices"];
prices seems to be a List of something.something.ProductPrice. But it looks like you want the first one. So its just
var prices = productInfo["Prices"].First();
I dont see why you need that hugely complex LINQ chain. Or are you truying to extarct someting from the ProductPrice class
Related
I need help with filtering list data in c#.
I got 3 class named Product.cs, Storage.cs and Inventory.cs.
public class Storage{
string StorageId;
string Name;
}
public class Inventory{
string InventoryId;
string StorageId;
string ProductId;
}
I got the filled List<Storage> mStorages, List<Product> mProduct and List<Inventory> mInventories.
I have trouble to print mStorages that contain with specific productId that only can be obtained from mInventories.
So, I tried this:
List<Storage> mFilteredStorage;
for(int i=0;i<mStorages.Count;i++){
if(mStorages[i] contain (productId from inventories)){
mFilteredStorage.add(mstorages[i]);
}
So I can get mFilteredStorage that contains specific product from inventories. (in inventories there are lot of product id).
What should I do to get that filteredStorage? I tried to use list.contains() but it only return true and at last there are duplicated storage at mFilteredStorage.
Really need your help guys. Thanks in advance.
I suggest you to read about lambda-expressions, that is what you are looking for.
mFilteredStorage.AddRange(mStorages.Where(storage => inventories.Any(inventory => inventory.productId == storage.productId)).ToList());
This returns you a list with your filtered conditions. So right after Where you iterate over each item in your list, I called this item storage. (you can name those what ever you want to) Then we iterate over your object inventories with another lambda expression. This, the second lambda expression, returns either true if any of inventories's productIds match the productId of the current iterating object of mStorages or false if they don't match.
So you once the productIds match you can imagine the code like the following:
mStorages.Where(storage => true);
And once the result of the second lambda expression is true, storage will be added to the IEnumerable you will get as a result of the Where method.
Since we get an IEnumerable as return, but we want to add those Storage objects to mFilteredStorage, I convert the IEnumerable to a list, by:
/*(the return object we get from the `Where` method)*/.ToList();
You can use LINQ to accomplish your goal. Since Storage has no ProductId, the query will match by StorageId.
var filteredStoragesQry =
from storage in mStorages
where inventories.Any(inventory => inventory.StorageId == storage.StorageId)
select storage;
mFilteredStorages = filteredStoragesQry.ToList();
This query is for LINQ to objects, but it will also work in Entity Framework, when you replace mStorages and inventories by the respective DbSet objects from the context.
mStorages.Join(mInventories, x => x.StorageId, y => y.StorageId, (x, y) => new { Storage = x, ProductId = y.ProductId})
.Where(z => z.ProductId == "specificProductId").Select(z => z.Storage).ToList()
I ended with this code.
mFilteredStorage = tempStorage.GroupBy(s => s.Id).Select(group => group.First()).ToList()
This code is what I want to show.
Suppose I have one collection, call it ids it is of type IEnumerable<string>, I have a second collection call it objects it's of type MyObject[]. MyObject has a string property called id. I would like a LINQ statement that returns all off the objects in the objects collection who's id matches any value in the ids collection. ids will be a strict subset of objects.Select(x => x.id). Meaning, for every string in ids I know there will be exactly one corresponding MyObject in objects. Can someone post a pure LINQ solution? I've tried a couple things with no luck. I can come up with an iterative solution easily enough so unless it's impossible to do with only LINQ please don't post any.
"Just" LINQ:
var r = obj.Where(o => ids.Any(id => id == o.id));
But better, for larger n, with a set:
var hs = new HashSet(ids);
var r = obj.Where(o => hs.Contains(o.id));
I think this is pretty straightforward with query syntax.
It would look something like:
var a = from o in objects
join i in ids on o.id equals i
select o;
If you just want a list of MyObject that match, you can do :
var solution = objects.Where(x=> ids.Contains(x.id));
With this instead, you'll get a List<T> where T is an Anonymous type with 2 properties, Id that is the string that work as "key" in this specific case, and Obj, a list of MyObject which id correspond to the Id property.
var solution = ids.Select(x=>new{ Id = x, Obj=objects.Where(y=>y.id == x).ToList()})
.ToList();
If you just want to know if there is any object in the intersection (which was what I was looking for)
Based on this
var a = from o in objects
join i in ids on o.id equals i
select o;
You can do this as well
var isEmpty = objects.Any(x => ids.Any(y => y == x.ToString()));
The accepted answer is correct. However, if someone doesn't like using SQL style LINQ, here is the LINQ extension method approach to solving the same problem.
var filteredObjects = objects.Join(ids, obj => obj.Id, id => id, (obj, _) => obj);
We are joining two different types, so the 2nd & 3rd Join parameter signify that join will be made on id.
The fourth parameter is used to select an object out of the resultant (obj, id) pair after applying join.
In C#, I have an object type 'A' that contains a list of key value pairs.
The key value pairs is a category string and a value string.
To instantiate object type A, I would have to do the following:
List<KeyValuePair> keyValuePairs = new List<KeyValuePair>();
keyValuePairs.Add(new KeyValuePair<"Country", "U.S.A">());
keyValuePairs.Add(new KeyValuePair<"Name", "Mo">());
keyValuePairs.Add(new KeyValuePair<"Age", "33">());
A a = new A(keyValuePairs);
Eventually, I will have a List of A object types and I want to manipulate the list so that i only get unique values and I base it only on the country name. Therefore, I want the list to be reduced to only have ONE "Country", "U.S.A", even if it appears more than once.
I was looking into the linq Distinct, but it does not do what I want because it I can't define any parameters and because it doesn't seem to be able to catch two equivalent objects of type A. I know that I can override the "Equals" method, but it still doesn't solve the my problem, which is to render the list distinct based on ONE of the key value pairs.
To expand upon Karl Anderson's suggestion of using morelinq, if you're unable to (or don't want to) link to another dll for your project, I implemented this myself awhile ago:
public static IEnumerable<T> DistinctBy<T, U>(this IEnumerable<T> source, Func<T, U>selector)
{
var contained = new Dictionary<U, bool>();
foreach (var elem in source)
{
U selected = selector(elem);
bool has;
if (!contained.TryGetValue(selected, out has))
{
contained[selected] = true;
yield return elem;
}
}
}
Used as follows:
collection.DistinctBy(elem => elem.Property);
In versions of .NET that support it, you can use a HashSet<T> instead of a Dictionary<T, Bool>, since we don't really care what the value is so much as that it has already been hashed.
Check out the DistinctBy syntax in the morelinq project.
A a = new A(keyValuePairs);
a = a.DistinctBy(k => new { k.Key, k.Value }).ToList();
You need to select the distinct property first:
Because it's a list inside a list, you can use the SelectMany. The SelectMany will concat the results of subselections.
List<A> listOfA = new List<A>();
listOfA.SelectMany(a => a.KeyValuePairs
.Where(keyValue => keyValue.Key == "Country")
.Select(keyValue => keyValue.Value))
.Distinct();
This should be it. It will select all values where the key is "Country" and concat the lists. Final it will distinct the country's. Given that the property KeyValuePairs of the class A is at least a IEnumerable< KeyValuePair< string, string>>
var result = keyValuePairs.GroupBy(x => x.Key)
.SelectMany(g => g.Key == "Country" ? g.Distinct() : g);
You can use the groupby statement. From here you can do all kind off cool stuf
listOfA.GroupBy(i=>i.Value)
You can groupby the value and then sum all the keys or something other usefull
I have a List<int> ListOfIDs containing some numbers which are IDs.
I have a List<CustomClass> ListOfObjects containing some objects, which properties reflecting their IDs.
I've searched high and low for a Linq query that will allow me to return from my List a sublist of only those objects which have an ID that is contained within the List.
My attempt does not compile and I cannot seem to correct the syntax :
List<CustomClass> SubList = ListOfObjects.Where(ListOfIDs.Contains(p => p.ID))
Thanks very much.
I think you want to do like this?
List<CustomClass> SubList = ListOfObjects
.Where(obj => ListOfIDs.Contains(obj.ID))
.ToList();
I think this is what you need:
List<CustomClass> SubList = ListOfObjects.Where(p => ListOfIDs.Contains(p.ID)).ToList();
Don't forget to call ToList() in the end.
Also consider using HashSet for ListOfIDs, because complexity of Contains operation is just O(1). But, well it depends on how much data you have.
Here's the correct syntax for what you're trying to do:
... ListOfObjects.Where(p => ListOfIDs.Contains(p.ID)).ToList();
Though this might be faster that the Where(Contains) method:
var sublist = (
from obj in ListOfObjects
join id in ListOfIDs on id equals obj.ID
select obj ).ToList();
Try to use this piece of code snippet:
List<CustomClass> SubList = ListOfObjects.Where(o => ListOfIDs.Contains(o.ID))
.ToList();
I have a List of custom objects:
List<SomeObject> someobjects = getAllSomeObjects();
List<SomeObject> someobjectsfiltered = new List<SomeObject>();
class SomeObject
{
List <AnotherList>
}
class AnotherList
{
public string Name{get;set;}
public Categories category {get;set;}
}
So I'm trying to get All AnotherList items of a specific type using Lambda
someobjectsfiltered = someobjects.SelectMany(s => s.AnotherList.FindAll(a => a.category == SomeCategory));
But I get the
Can not implicitly convert type
IEnumerable to Generic.List
error
Any idea how to solve this?
Many thanks.
You need to throw a ToList() on the end or change the type of the result to IEnumerable<SomeObject> because, as the error says, you can't assign an IEnumerable<T> to a variable of type List<T>.
someobjectsfiltered = someobjects.SelectMany(s => s.AnotherList.FindAll(a => a.category == SomeCategory))
.ToList();
Edit based on comments
If what you want is the SomeObjects that have a list containing an item that matches the category you can do that using.
someobjectsfiltered = someobjects.Where( s => s.AnotherList.Any( a => a.category == SomeCategory ))
.ToList();
#tvanfosson #Maya Not sure how "Any" would work here since it will come back with true all the time causing the whole AnotherList (the inner list) to be selected back, if that "Category" type exist once in the inner list then all of its contents will be selected including those with the unwanted Category types.
SelectMany returns IEnumerable<T> - you can convert that to a List<T> by simply adding a call to ToList() onto the end.
someobjectsfiltered = someobjects.SelectMany(s => s.AnotherList.FindAll(a => a.category == SomeCategory)).ToList();