Lambda for nested arrays - c#

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();

Related

Cannot retrieve object from inner dictionaries/lists

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

C# OrderByDescending on value from list in another list

I'm trying to order a list (will refer to this list as result) by a value in another list (Redeems).
The result list contains the Redeems list, and I want to order result by the field "SumChosenYear" in the "Redeems" list and get top 20. This is what I've managed to get, and in theory I think it should work.
result = result
.OrderByDescending(input => input.Redeems
.Select(input2 => input2.SumChosenYear)
.ToList())
.Take(20)
.ToList();
However it throws an exception saying "Atleast one object implement IComparable". Why does this happen?
This line:
input.Redeems
.Select(input2 => input2.SumChosenYear)
.ToList()
returns you a List and because List does not implement IComparable you cannot put this lambda inside this overload of OrderByDescending extension.
You have basically two options:
First option
Cretae your custom implementation of IComparer for this list (assuming SumChosenYear property is an int for this purpose):
public class SumChosenYearListComparer : IComparer<List<int>>
{
public int Compare(List<int> x, List<int> y)
{
//Your custom comparison...
}
}
and then use it with this overload of OrderByDescending extension:
var result = result
.OrderByDescending(input => input.Redeems
.Select(input2 => input2.SumChosenYear)
.ToList(), new SumChosenYearListComparer())
.Take(20)
.ToList();
Second option
You can choose which item in this list you want to use in the comparison(maybe the max, min, first or last value or maybe even the sum of all the items).
Assuming you want to comapre using the max value in the list your code could look something like this:
var result = result
.OrderByDescending(input => input.Redeems
.Max(input2 => input2.SumChosenYear))
.Take(20)
.ToList();

Making a list distinct in C#

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

LINQ query on PropertyInfo list

I have a sub class which is 'derived' (is that the right word) from a base class two levels up. I have a list of all the properties in this class (so that includes properties from the parent, and the parent of the parent). What I want is just the properties where the DeclaringType is "CrazyNinjaBadger" (i.e. only the properties from my sub - class).
I've tried this statement:
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger");
But I just get
"Cannot implicitly convert type
'System.Collections.Generic.IEnumerable' to
'System.Reflection.PropertyInfo[]'.
Please can someone suggest a statement that will work?
Use Where to filter properties, and convert result to array:
PropertyInfo[] properties = type.GetProperties()
.Where(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
Also I believe you want to use type name like this x.DeclaringType.Name == "CrazyNinjaBadger". Btw Select operator projects properties to sequence of boolean values in your case. So, your query actually returns IEnumerable<bool> with results of type string comparison to CrazyNinjaBadger.
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger");
Select(...) returns an implemenetation of IEnumerable<T>. The compiler error is very explicit.
Another point is you want to filter properties. .Select(...) is for projecting an enumerable into another of the same or other type.
For example:
IEnumerable<string> strings = new string[] { "0", "1" };
// Converting the string enumerable to an enumerable of integers:
IEnumerable<int> integers = strings.Select(some => int.Parse(some));
// Also, convert each strings into an anonymous object!
IEnumerable<object> whoKnows = strings.Select(some => new { Value = some });
In order to filter an enumerable you need to use .Where(...).
In the other hand, x.DeclaringType.ToString() == "CrazyNinjaBadger" is correct but it should be x.DeclaringType.Name == "CrazyNinjaBadger" (you don't need to convert the type to string as Type has a property Name).
Finally I'd argue that you don't need to set the result in an array, you can just do this:
IEnumerable<PropertyInfo> properties =
type.GetProperties()
.Where(x => x.DeclaringType.Name == "CrazyNinjaBadger");
PropertyInfo[] properties = type.GetProperties()
.Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
The ToArray() needs to be added to convert to Array...
You're getting that error because Select() always returns an IEnumerable.
Just Add .ToArray() at the end of the line to make it work.
Add ToArray() at the end of the line
PropertyInfo[] properties = type.GetProperties()
.Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger")
.ToArray();
You were almost there! Select returns an IEnumerable, but you're trying to set the value of a PropertyInfo array. All you need is an additional call to ToArray and you're there!
PropertyInfo[] properties = type.GetProperties().Select(x => x.DeclaringType.ToString() == "CrazyNinjaBadger").ToArray()

C# .NET 3.5 - Performing list like operations on members of a list

I have a field object and I create a list of fields:
class Field {
string objectName;
string objectType;
string fieldName;
string fieldValue;
//constructor...
}
List<Field> fieldList = new List<Field>();
Suppose I wanted to query this list to return a collection of distinct object names (to then be inserted into a checkedlistbox. How would I go about doing that?
I imagine some LINQ magic can manage this?
The expression should return a List of distinct object names from the list as defined. I converted it to a list since the docs for the CheckedListBox DataSource property indicated that it needs to implement IList or IListSource, not merely IEnumerable.
((ListControl)cbListBox).DataSource = fieldList.Select( f => f.objectName )
.Distinct()
.ToList() );
If accessing the checkedListBox as a ListControl doesn't give access to the DataSource (sometimes the docs lie), you could try:
cbListBox.Items.AddRange( fieldList.Select( f => f.objectName )
.Distinct()
.ToArray() );
Either of these work
Using var
1) var fieldNameCollection = from f in fieldList select f.FieldName;
2) Lambda syntax
var fieldNameCollection = fieldList.Select(f => f.FieldName);
Alternately, instead of using var, you can also use
IEnumerable fieldNameCollection = fieldList.Select(f => f.FieldName);
var q = from Field f in fileldList select f.objectName;
chkBoxList.DataSource = q.Distinct();

Categories

Resources