Linq query List<List<class>> Where property of class C# - c#

Could someone suggest a way for me to select one of the lists within a parent list where one of it's elements has a certain property value?
public class HierarchyLevel
{
public string Abbreviation;
public string Name;
public string Value;
public Type LevelType;
public List<HierarchyLevel> Children = new List<HierarchyLevel>();
}
public static List<List<HierarchyLevel>> ElementaryTypes = new List<List<HierarchyLevel>>();
I am actually trying to get the List that has the LevelType field of a specific type.

You wrote:
I am actually trying to get the List that has the LevelType field of a specific type.
What do you want if you've got several HierarchyLevels with this LevelType? And what do you want if there are no HierarchyLevels at all with this LevelType?
Let's assume that you want all HierarchyLevels with this LevelType. If later on you only want the first, or the one with a certain Abbreviation (or whatever), you could always use .FirstOrDefault, or .Where. and do a ToList in the end.
Implementing it as an extension function. See Extension Methods Demystified
public static IEnumerable<HierarchyLevel> GetHierarchyLevelsWithLevelType(
this IEnumerable<HierarchyLevel> hierarchyLevels,
LevelType desiredLevelType)
{
foreach (var hierarchyLevel in hierarchyLevels)
{
if (hierarchyLevel.LevelType == desiredLevelType)
{ // found one!
yield return hierarchyLevel;
}
// using recursion: check all the Children
IEnumerable<HierarchyLevel> childrenWithDesiredLevelType = hierarchyLevel.Children
.GetHierarchyLevelsWithLevelType(desiredLevelType);
foreach(var childWithDesiredLevelType in childrenWithDesiredLevelType)
{
yield return childWithDesiredLevelType;
}
}
}
Because of the recursion all Grandchildren and their Children etc will be returned
usage:
var allSpecialHierarchies = myHierarchies.GetHierarchyLevelsWithLevelType(LevelType.Special);
// get the first:
var firstSpecialHierarchy = allSpecialHierarchies.FirstOrDefault();
// get the first three named "Shakespeare:
var threeShakesPeares = allSpecialHierarchies
.Where(hierarchyLevel => hierarchyLevel.Name == "Shakespeare")
.Take(3)
For better usage you should provide a version that has a parameter IQualityComparer<LevelType>. Let the function above call that one.
And a nice challenge: to be fully LINQ compatible, create a version with a predicate that returns a type T and an equality comparer for this type T, so that you can have all HierarchyLevels with a certain Name, or Abbreviation.

You can Solve your with the help of recursion consider example below :- I have taken sample type of String you can use any of your Type
List<List<HierarchyLevel>> sample = new List<List<HierarchyLevel>>();
Type yourType = typeOf(string);
List<HierarchyLevel> filtered = sample.Where(x => ContainsElement(x, yourType));
public void bool ContainsElement(List<HierarchyLevel> list,Type yourType)
{
if(list.Any(x => x.LevelType == yourType) //check if current node has same level type
return true;
else if(list.Childern.Count > 0) //check if current node has children if yes then call ContainsElement again
return list.Children.Any(x => ContainsElement(x,yourType));
else
return false; //else return false in last
}

Thanks to user743414 for pointing out how simple this was :)
By using a dictionary instead, I could reference to the specific list. (This is also a faster option.)
Dictionary<Type,List<HierarchyLevel>> HierarchicalData;
I can now use it with a key of 'Type':
private void UpdateGeneralData(object Entity, Dictionary<Type,List<HierarchyLevel>> TypeData)
{
CBType.Items.Clear();
foreach (var item in TypeData[Entity.GetType()])
{
CBType.Items.Add(item);
}
}

Something like this (?):
List<HierarchyLevel> var = hLevel.Select(h => h.Children.Where(c => c.Param = "desired param")).ToList();

Related

C# using list to find elements in class

I'm trying to use List.Contains to find if item in list, has it's bool true or false
Problem is, no matter if the bool is true or false, function always return's false.. her's an example
public class RecepieClass
{
public Recepie recepie;
public bool isUnlocked;
}
public class RecepieList : MonoBehaviour
{
public List<RecepieClass> recepies = new List<RecepieClass>();
public void SelectRecepie(Recepie r)
{
Debug.Log(recepies.Contains(new RecepieClass{recepie = r, isUnlocked = false}));
}
}
Use a for loop, and examine the recipe property of each RecipeClass item. If you know that the Recepie parameter being passed to the function is the actual instance of one of the RecepieClass items in your list, then the default Object.Equals method will work. If it is another instance, you'll need to define your own Equals method (i.e., Recepie should implement IEquatable).
public class RecepieClass
{
public Recepie recepie;
public bool isUnlocked;
}
public class RecepieList : MonoBehaviour
{
public List<RecepieClass> recepies = new List<RecepieClass>();
public void SelectRecepie(Recepie r)
{
foreach (var rc in recepies) {
if (rc.recepie.Equals(r)) {
// you've found it
if (rc.isUnlocked) {
// do something
} else {
// do something else
}
break;
}
}
}
}
If you're into the whole brevity thing, you could use System.Linq.FirstOrDefault (just be prepared for it to return a null).
public void SelectRecepie(Recepie r)
{
if (recepies.FirstOrDefault(rc => rc.recepie.Equals(r))?.isUnlocked ?? false) {
// do something
}
}
When comparing reference types the comparison is determining if objects point to the same object (assuming class doesn't override IEquatable). What you are tying to find is an item with the same values.
The safest way to find an item within a collection is to use a unique identifier within the item combined with the FirstOrDefault() method.
Given there is nothing that seems unique on RecepieClass the example below assumes there is an Id within Recepie referenced from RecepieClass.
var recipe = recepies.FirstOrDefault(r => r.recepie?.Id == 123456);
Debug.Log(recipe);
With the above example recipe will be null or the first RecepieClass with a Recepie matching the supplied id value (123456 in the example).
I dont know if your value r is unique in List or not. i just trap the first found
you just write: (with using System.Linq)
public void SelectRecepie(Recepie r)
{
var result = recepies.FirstOrDefault(x => x.recepie.Equals(r)).isUnlocked;
Debug.Log(result);
}

.NET IComparable: how to implement

I have a collection of objects I need to order but not sure how.
There is a string property called, say Prop1, that I want to sort by. And I want to sort based on a List of strings which contain all possible values of Prop1.
List<string> precedence = new List<string>() { "firstPrecedence", "secondPrecedence" ....
How would I implement my CompareTo(object obj) method?
I'm trying with this but don't really know what i'm doing!
public int CompareTo(object obj)
{
List<string> precedence = new List<string>() { "firstPrecedence", "secondPrecedence", "thirdPrecedence" };
Filter filterOther = obj as Filter;
foreach (var item in precedence)
{
return String.Compare(filterOther.FilterValue, item);
}
return 0;
}
Well, if your precedence list is known at compile time and you can use it, then you can compare the indexes of the values you are sorting:
private static List<string> Precedence = new List<string>() { "item1", "item2", "item3" }; // etc
public int CompareTo(object obj)
{
Filter item = obj as Filter; // Assume not null.
int otherIndex = Precedence.IndexOf(item.FilterValue);
int thisIndex = Precedence.IndexOf(this.FilterValue); // Assume 'this' is a Filter
// This may need to be otherIndex.CompareTo(thisIndex) depending on the direction of sort you want.
return thisIndex.CompareTo(otherIndex);
}
If the FilterValue value is not in the list, IndexOf will return -1, which will still work in the sorting implementation here, but may sort at the top or bottom of the list... I can never remember which!
Note that the CompareTo method returns either 0, something less than 0, or something greater than 0. Usually, -1, 0, and 1.
Also, there is a generic IComparable<> which will allow you to achieve this in a more strongly-typed way:
public class Filter : IComparable<Filter>
{
}
And I'm sure some clever person will give you a solution in LINQ...
try this (assuming you have a List<Filter> )
filterObjectList.Sort((f1,f2) => precedence.IndexOf(f1.FilterValue).CompareTo(precedence.IndexOf(f2.FilterValue));
Using LINQ:
precedence.SelectMany(p => objs.Where(o => o.Prop1 == p));
OR
objs.Select(s => new { Index = precedence.IndexOf(s.Prop1), Obj = s })
.OrderBy(a => a.Index).Select(a => a.Obj);
Create a new class of your object that you want to sort:
public class MySortableObject: IComparable {
private string str;
public MySortableObject(string _str) {
this.str = _str;
}
int IComparable.CompareTo(object obj) {
MySortableObject comparedObj = (MySortableObject) obj;
// Implement here the code that will compare the current object (this) and the compared object (comparedObj)
// It must return -1 if this instance precedes comparedObj in the sort order
// It must return 1 if this instance follows comparedObj in the sort order
// It must return 0 if this instance occurs in the same position in the sort order as comparedObj
// Use for example String.CompareTo() method to implement this, or your own code (with if(), switch()... whatever you need)
}
}

How to update an element with a List using LINQ and C#

I have a list of objects and I'd like to update a particular member variable within one of the objects. I understand LINQ is designed for query and not meant to update lists of immutable data. What would be the best way to accomplish this? I do not need to use LINQ for the solution if it is not most efficient.
Would creating an Update extension method work? If so how would I go about doing that?
EXAMPLE:
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).Update(
trade => trade.Buy += optionQty);
Although linq is not meant to update lists of immutable data, it is very handy for getting the items that you want to update. I think for you this would be:
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).ToList().ForEach( trade => trade.Buy += optionQty);
I'm not sure if this is the best way, but will allow you to update an element from the list.
The test object:
public class SomeClass {
public int Value { get; set; }
public DateTime Date { get; set; }
}
The extension method:
public static class Extension {
public static void Update<T>(this T item, Action<T> updateAction) {
updateAction(item);
}
}
The test:
public void Test()
{
// test data
List<SomeClass> list = new List<SomeClass>()
{
new SomeClass {Value = 1, Date = DateTime.Now.AddDays(-1)},
new SomeClass {Value = 2, Date = DateTime.Now },
new SomeClass {Value = 3, Date = DateTime.Now.AddDays(1)}
};
// query and update
(from i in list where i.Date.Day.Equals(DateTime.Now.Day) select i).First().Update(v => v.Value += 5);
foreach (SomeClass s in list) {
Console.WriteLine(s.Value);
}
}
So you're expecting to get a single result here. In that case you might consider utilizing the SingleOrDefault method:
var record =
(from trade in CrudeBalancedList
where trade.Date.Month == monthIndex
select trade).SingleOrDefault();
if (record != null)
record.Buy += optionQty;
Note that the SingleOrDefault method expects there to be exactly one or zero value returned (much like a row in a table for some unique primary key). If more than one record is returned, the method will throw an exception.
To create such a method, you would start with its prototype:
public static class UpdateEx {
public void Update(this IEnumerable<T> items,
Expression<Action> updateAction) {
}
}
That's the easy part.
The hard part will be to compile the Expression<Action> into an SQL update statement. Depending on how much syntax you want to support, such a compiler's complexity can range from trivial to impossible.
For an example of compiling Linq Expressions, see the TableQuery class of the sqlite-net project.

Using Except() on a Generic collection

I have asked this question about using the a Linq method that returns one object (First, Min, Max, etc) from of a generic collection.
I now want to be able to use linq's Except() method and I am not sure how to do it. Perhaps the answer is just in front on me but think I need help.
I have a generic method that fills in missing dates for a corresponding descriptive field. This method is declared as below:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection, string datePropertyName, string descriptionPropertyName)
{
Type type = typeof(T);
PropertyInfo dateProperty = type.GetProperty(datePropertyName);
PropertyInfo descriptionProperty = type.GetProperty(descriptionPropertyName);
...
}
What I want to accomplish is this. datePropertyName is the name of the date property I will use to fill in my date gaps (adding default object instances for the dates not already present in the collection). If I were dealing with a non-generic class, I would do something like this:
foreach (string description in descriptions)
{
var missingDates = allDates.Except(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList());
...
}
But how can I do the same using the generic method FillInMissingDates with the dateProperty and descriptionProperty properties resolved in runtime?
I think the best way would be to define an interface with all of the properties that you want to use in your method. Have the classes that the method may be used in implement this interface. Then, use a generic method and constrain the generic type to derive from the interface.
This example may not do exactly what you want -- it fills in missing dates for items in the list matching a description, but hopefully it will give you the basic idea.
public interface ITransactable
{
string Description { get; }
DateTime? TransactionDate { get; }
}
public class CompletedTransaction : ITransactable
{
...
}
// note conversion to extension method
public static void FillInMissingDates<T>( this IEnumerable<T> collection,
string match,
DateTime defaultDate )
where T : ITransactable
{
foreach (var trans in collection.Where( t => t.Description = match ))
{
if (!trans.TransactionDate.HasValue)
{
trans.TransactionDate = defaultDate;
}
}
}
You'll need to Cast your enumeration to ITransactable before invoking (at least until C# 4.0 comes out).
var list = new List<CompletedTransaction>();
list.Cast<ITransactable>()
.FillInMissingDates("description",DateTime.MinValue);
Alternatively, you could investigate using Dynamic LINQ from the VS2008 Samples collection. This would allow you to specify the name of a property if it's not consistent between classes. You'd probably still need to use reflection to set the property, however.
You could try this approach:
public IEnumerable<T> FillInMissingDates<T>(IEnumerable<T> collection,
Func<T, DateTime> dateProperty, Func<T, string> descriptionProperty, string desc)
{
return collection.Except(collection
.Where(d => descriptionProperty(d) == desc))
.Select(d => dateProperty(d));
}
This allows you to do things like:
someCollection.FillInMissingDates(o => o.CreatedDate, o => o.Description, "matching");
Note that you don't necessarily need the Except() call, and just have:
.. Where(d => descriptionProperty(d) != desc)
foreach (string description in descriptions)
{
var missingDates = allDates.Except<YourClass>(originalData.Where(d => d.Description == desc).Select(d => d.TransactionDate).ToList());
}
In fact, almost all LINQ extension in C# have a generic possible value. (Except and Except)
If you're going to identify the property to be accessed by a string name, then you don't need to use generics. Their only purpose is static type safety. Just use reflection to access the property, and make the method work on a non-generic IEnumerable.
Getting Except result with multiple properties working with custom data class is not allowed.
You have to use it like this: (given in msdn 101 LINQ Samples)
public void Linq53()
{
List<Product> products = GetProductList();
List<Customer> customers = GetCustomerList();
var productFirstChars =
from p in products
select p.ProductName[0];
var customerFirstChars =
from c in customers
select c.CompanyName[0];
var productOnlyFirstChars = productFirstChars.Except(customerFirstChars);
Console.WriteLine("First letters from Product names, but not from Customer names:");
foreach (var ch in productOnlyFirstChars)
{
Console.WriteLine(ch);
}
}
Having the key, you can handle your data accordingly :)

LINQ: How to declare IEnumerable[AnonymousType]?

This is my function:
private IEnumerable<string> SeachItem(int[] ItemIds)
{
using (var reader = File.OpenText(Application.StartupPath + #"\temp\A_A.tmp"))
{
var myLine = from line in ReadLines(reader)
where line.Length > 1
let id = int.Parse(line.Split('\t')[1])
where ItemIds.Contains(id)
let m = Regex.Match(line, #"^\d+\t(\d+)\t.+?\t(item\\[^\t]+\.ddj)")
where m.Success == true
select new { Text = line, ItemId = id, Path = m.Groups[2].Value };
return myLine;
}
}
I get a compile error,because "myLine" is not a IEnumerable[string] and I don't know how to write IEnumerable[Anonymous]
"Cannot implicitly convert type 'System.Collections.Generic.IEnumerable[AnonymousType#1]' to 'System.Collections.Generic.IEnumerable[string]'"
You cannot declare IEnumerable<AnonymousType> because the type has no (known) name at build time. So if you want to use this type in a function declaration, make it a normal type. Or just modify your query to return a IENumerable<String> and stick with that type.
Or return IEnumerable<KeyValuePair<Int32, String>> using the following select statement.
select new KeyValuePair<Int32, String>(id, m.Groups[2].Value)
I am not necessarily recommending this...
It is a kind of subversion of the type system but you could do this:
1) change your method signature to return IEnumerable (the non generic one)
2) add a cast by example helper:
public static class Extensions{
public static IEnumerable<T> CastByExample<T>(
this IEnumerable sequence,
T example) where T: class
{
foreach (Object o in sequence)
yield return o as T;
}
}
3) then call the method something like this:
var example = new { Text = "", ItemId = 0, Path = "" };
foreach (var x in SeachItem(ids).CastByExample(example))
{
// now you can access the properties of x
Console.WriteLine("{0},{1},{2}", x.Text, x.ItemId, x.Path);
}
And you are done.
The key to this is the fact that if you create an anonymous type with the same order, types and property names in two places the types will be reused. Knowing this you can use generics to avoid reflection.
Hope this helps
Alex
The method signature on SearchItem indicates that the method returns an IEnumerable<string> but the anonymous type declared in your LINQ query is not of type string. If you want to keep the same method signature, you have to change your query to only select strings. e.g.
return myLine.Select(a => a.Text);
If you insist on returning the data selected by your query, you can return an IEnumerable<object> if you replace your return statement with
return myLine.Cast<object>();
Then you can consume the objects using reflection.
But really, if your going to be consuming an anonymous type outside the method that it is declared in, you should define a class an have the method return an IEnumerable of that class. Anonymous types are convenience but they are subject to abuse.
Your function is trying to return IEnumerable<string>, when the LINQ statement you are executing is actually returning an IEnumerable<T> where T is a compile-time generated type. Anonymous types are not always anonymous, as they take on a specific, concrete type after the code is compiled.
Anonymous types, however, since they are ephemeral until compiled, can only be used within the scope they are created in. To support your needs in the example you provided, I would say the simplest solution is to create a simple entity that stores the results of your query:
public class SearchItemResult
{
public string Text { get; set; }
public int ItemId { get; set; }
public string Path { get; set; }
}
public IEnumerable<SearchItemResult> SearchItem(int[] itemIds)
{
// ...
IEnumerable<SearchItemResult> results = from ... select new SearchItemResult { ... }
}
However, if your ultimate goal is not to retrieve some kind of object, and you are only interested in, say, the Path...then you can still generate an IEnumerable<string>:
IEnumerable<string> lines = from ... select m.Groups[2].Value;
I hope that helps clarify your understanding of LINQ, enumerables, and anonymous types. :)
Return a ValueTuple instead of an anonymous class. Ex (using "named tuples")-
(Text: line, ItemId: id, Path: m.Groups[2].Value)
https://learn.microsoft.com/en-us/dotnet/csharp/tuples
Instead of-
new { Text = line, ItemId = id, Path = m.Groups[2].Value }
The ValueTuple is part of C# version 7 and was originally implemented as a separate NuGet package (System.ValueTuple). Starting with .NET 4.7 it is a built-in type. For .NET Core, versions prior to 2.0 required the NuGet package but it is built-in with version 2.0.
The question was asked a long time ago, I hope it helps someone...
"You cannot declare IEnumerable", instead, must convert it to a "custom" IEnumerable:
public class MyString
{
public string String { get; set; } = string.Empty;
}
public IEnumerable<MyString> GetMyStrings(List<string> Strings)
{
var AnonString = from S in Strings group S by S into Grouped select new { String = Grouped.Key };
IEnumerable<MyString> Result = AnonString.Select(x => new MyString() { String = x.String }).ToArray();
return Result;
}
Regards.
this link could be useful for others who end up here
https://idreesdotnet.blogspot.com/2019/08/c-how-to-create-list-of-anonymous-type.html
the first solution (of 6) is delightlfully simple
1: First create the object(s) of anonymous type and then pass it to an array and call ToList() method.
var o1 = new { Id = 1, Name = "Foo" };
var o2 = new { Id = 2, Name = "Bar" };
var list = new[] { o1, o2 }.ToList();

Categories

Resources