LINQ - Conditional navigation in list - c#

Let's say I have a list of objects which inherits from the same base class. Is it then possible to get the value that is only specified in one of the subclasses through LINQ? In my example, i want to find the instance which has a specific object which has a specific property?
I have made this example in Linqpad:
void Main()
{
var list = new List<A>
{
new B
{
MyProp = new D{ OtherProp = 1}
},
new C(),
new B
{
MyProp = new D{ OtherProp = 30}
},
};
list.Where(x => ....) // how to find the instance where OtherProp == 30 ?
}
public class A
{
public int JustAprop { get; set; }
}
public class B : A
{
public D MyProp { get; set; }
}
public class C : A
{
}
public class D
{
public int OtherProp { get; set; }
}

You can use Where method and try to cast every item to B class, then OtherProp value inside MyProp
var result = list.Where(l => (l as B)?.MyProp?.OtherProp == 30);
This can be rewritten a little bit using pattern matching with is operator
var result = list.Where(l => l is B b && b.MyProp.OtherProp == 30);
Another way is to use OfType<T> method to get a list of B instances only, then check OtherProp inside MyProp
var result = list.OfType<B>().Where(b => b.MyProp.OtherProp == 30);

Related

How to correctly use Expression Trees on properties (without reflection)

Objective: process an object and if the object implements an expected type, I want to change a specific property value (this part is working fine), and I also would like to apply the same logic to all property lists (that I explicit point) that are of the same expected type.
I have the following code:
public abstract class BaseObject
{
public int Id { get; set; }
}
public class Father : BaseObject
{
public DateTime CreatedOn { get; set; }
public string Name { get; set; }
public IEnumerable<ChildA> Children1 { get; set; }
public IEnumerable<ChildB> Children2 { get; set; }
public IEnumerable<ChildA> Children3 { get; set; }
public IEnumerable<ChildB> Children4 { get; set; }
}
public class ChildA : BaseObject
{
public int Val1 { get; set; }
}
public class ChildB : BaseObject
{
public string Name { get; set; }
public int Total { get; set; }
}
I want to process an object by applying some changes on a specific property on the target object and on all property children that I explicit say:
public void Start()
{
var listA = new List<ChildA> { new ChildA { Id = 1, Val1 = 1 }, new ChildA { Id = 2, Val1 = 2 } };
var listB = new List<ChildB> { new ChildB { Id = 1, Name = "1", Total = 1 } };
var obj = new Father { Id = 1, CreatedOn = DateTime.Now, Name = "F1", ChildrenA = listA, ChildrenB = listB };
// I explicit tell to process only 2 of the 4 lists....
ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });
}
I was able to write this function:
public void ProcessObj<T>(T obj, Expression<Func<T, object[]>> includes = null)
{
var objBaseObject = obj as BaseObject;
if (objBaseObject == null) return;
// Here I change the ID - add 100 just as an example....
objBaseObject.Id = objBaseObject.Id + 100;
if (includes == null) return;
var array = includes.Body as NewArrayExpression;
if (array == null) return;
var exps = ((IEnumerable<object>)array.Expressions).ToArray();
for (var i = 0; i < exps.Count(); i++)
{
var name = ((MemberExpression)exps[i]).Member.Name;
var childProperty = obj.GetType().GetProperties(
BindingFlags.Public | BindingFlags.Instance
).FirstOrDefault(prop => prop.Name == name);
if (childProperty == null) continue;
// NOT correct because I think I am getting a copy of the object
// and not pointing to the object in memory (by reference)
var childList = childProperty.GetValue(obj);
// TODO: loop on the list and apply the same logic as the father....
// change the ID field....
}
}
In this prototype I started writing reflection, but I really would like to avoid it if possible....
How can I do this???
Maybe I'm missing something, but it seems like you're complicating the problem by using expression trees. Can you just not use a regular Action and Func delegates to do this? Why do they need to be expression trees? Here's an example just using delegates:
public void ProcessObj<T>(T obj, Func<T, IEnumerable<object>> includes) {
var objBaseObject = obj as BaseObject;
if (objBaseObject == null) return;
// Create a reusable action to use on both the parent and the children
Action<BaseObject> action = x => x.Id += 100;
// Run the action against the root object
action(objBaseObject);
// Get the includes by just invoking the delegate. No need for trees.
var includes = includes(obj);
// Loop over each item in each collection. If the types then invoke the same action that we used on the root.
foreach(IEnumerable<object> include in includes)
{
foreach(object item in include)
{
var childBaseObject = item as BaseObject;
if(childBaseObject != null)
{
action(childBaseObject);
}
}
}
}
Useable just like before:
ProcessObj(obj, x => new object[] { x.Children1, x.Children2 });
No expression trees and no reflection, just regular delegate lambdas.
Hope that helps

How to compare all values in an object without repeating if statements?

I am trying to compare all possible values in a list of objects like this:
public class Object21
{
int Id,
bool firstbool,
bool secondbool
}
I would loop through the objects and compare them like this:
List<Object1> objects;
foreach(var o in objects)
{
if(firstbool && secondbool)
....
if(firstbool && !secondbool)
....
if(!firstbool && secondbool)
....
if(!firstbool && !secondbool)
....
}
This seems ok, but what if the object had several values that you were running through if statements.
public class Object2
{
int Id;
int firstbool;
....
int twentiethbool;
}
Then you would have to write out all of the possible conditional statements and your code would be terribly written and hard to read.
List<Object2> objects2;
foreach(var o in objects2)
{
if(firstbool && secondbool && ... && twentiethbool)
....
if(....)
....
....
....
if(!firstbool && !secondbool && ... && !twentiethbool)
....
}
Is there a simpler way to write the second scenario so that you are not writing every combination of if statements?
In the end I would like to calculate the percentage occurrence of each condition in the list.
To answer the first part of the question (about comparing every combination):
There isn't really a good way to do that, other than write a bunch of if statements. Of course; you probably shouldn't be doing that anyways :)
You could probably use reflection and recursion, but thats going to get messy really fast.
Luckily, to just get the percentage occurrence of each flag, you can just do:
list.Count(i => i.firstbool) / (double)list.Count();
...
first, create a dictionary to save all conditions
var dict = new Dictionary<string, int>{{"001",0},{"010",0} ...}
then, create key use bool values
var key=string.Empty;
key+=firstbool ?"0":"1"
key+=secondbool ?"0":"1"
......
after all, you can know which condition occurred
dict[key]++;
Given a class structure like this:
public class YourClass
{
public int Id { get; set; }
public bool firstbool { get; set; }
public bool secondbool { get; set; }
public bool thirdbool { get; set; }
}
You can use reflection to get all the boolean values (and only bool values) inside the class:
public IEnumerable<bool> GetBools(YourClass obj)
{
return obj.GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof (bool))
.Select(x => (bool)x.GetValue(obj, null));
}
Then use LINQ to iterate through the collection, and create a dictionary of combinations and totals:
List<YourClass> objects = new List<YourClass>();
var totals = objects.GroupBy(x => String.Join(",", GetBools(x)))
.ToDictionary(x => x.Key, x => x.Count() / (double)objects.Count);
This will give you a dictionary with each unique combination and the percentage it occurs.
Given this input:
var o = new List<YourClass>
{
new YourClass {firstbool = true, secondbool = true, thirdbool = false},
new YourClass {firstbool = false, secondbool = false, thirdbool = false},
new YourClass {firstbool = true, secondbool = true, thirdbool = false}
};
The result in the dictionary will be:
{["True,True,False", 0.666666666666667]}
{["False,False,False", 0.333333333333333]}
it's probably easier to rewrite your class, storing each condition in an array like follows:
public class MyObject
{
public static int numFields = 20;
public enum Conditions
{
C1, C2, C3, .... C20 //name for each condition, so can set values using descriptive names
};
public Boolean[] BinaryFields = new Boolean[numFields];
public void setCondition(Conditions condition, Boolean value)
{
BinaryFields[(int)condition] = value;
}
public override string ToString()
{
return string.Join(",", BinaryFields);
}
}
then you can calculate the stat by counting what is actually there, instead of numerating through all of the 2^20 possibilities. something like follows:
static void Main(string[] args)
{
//simulation: creat 10 MyObjects
List<MyObject> lst = new List<MyObject>();
for (int i = 0; i < 10; i++)
{
MyObject m = new MyObject();
//example of setting condition
m.setCondition(MyObject.Conditions.C1, true);
lst.Add(m);
}
//calculate stat
var resultCount = new Dictionary<string, int>(); //conditionResult, count
foreach (MyObject m in lst)
{
if (resultCount.ContainsKey(m.ToString()))
{
resultCount[m.ToString()] += 1;
}
else
{
resultCount.Add(m.ToString(), 1);
}
}
//print stat
foreach(KeyValuePair<string, int> entry in resultCount){
Debug.WriteLine("probability for conditoin={0} is {1}", entry.Key, (double)entry.Value / lst.Count);
}
}
If you have some unique action for each boolean properties combination I suggest you to use some kind of string key for your object, generated on those values. Something like "001001", "000000" etc. Then use Dictionary<string, Func<int>> to hold all your unique actions, get and perform the right one by it's key. For example:
public class Object21
{
public int Id { get; set; }
public bool FirstBool { get; set; }
public bool SecondBool { get; set; }
public bool ThirdBool { get; set; }
public bool FourthBool { get; set; }
public bool FifthBool { get; set; }
public bool SixthBool { get; set; }
public void Process()
{
// Perform the action
Actions[Key]();
}
// Returns "001001" like representation of your object
public string Key
{
get
{
return string.Join(string.Empty, GetType()
.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.Where(x => x.PropertyType == typeof(bool))
.Select(x => (bool)x.GetValue(this, null) ? "1" : "0" ));
}
}
private static Dictionary<string, Func<int>> Actions
{
get
{
return new Dictionary<string, Func<int>>
{
{"000000", new Func<int>(delegate
{
Console.WriteLine("000000 action performed.");
return 0;
})},
{"000001", new Func<int>(delegate
{
Console.WriteLine("000001 action performed.");
return 1;
})},
{"000010", new Func<int>(delegate
{
Console.WriteLine("000010 action performed.");
return 2;
})},
// More actions
{"111111", new Func<int>(delegate
{
Console.WriteLine("111111 action performed.");
return 63;
})}
};
}
}
}
And then use this in your program like this:
static void Main(string[] args)
{
var list = new List<Object21>
{
// initialize your list
};
foreach (var object21 in list)
{
object21.Process();
}
// Calculate your occurrences (basically what #Grant Winney suggested)
var occurrences = list.GroupBy(o => o.Key).ToDictionary(g => g.Key, g => (g.Count() / (double)list.Count)*100);
foreach (var occurrence in occurrences)
{
Console.WriteLine("{0}: {1}%", occurrence.Key, occurrence.Value);
}
}

Linq Join Not Equal

I have 3 classes:
public class HoteAvail
{
public int HotelID { get; set; }
public string Name { get; set; }
public List<Room> Rooms { get; set; }
}
public class Room
{
public int RoomID { get; set; }
public string RoomName { get; set; }
}
public class DAL
{
public static List<HoteAvail> GetAll()
{
return new List<HoteAvail>()
{
new HoteAvail{HotelID=1,Name="Taj",Rooms=new List<Room>(){new Room{RoomID=1,RoomName="Deliux"}}},
new HoteAvail{HotelID=2,Name="x",Rooms=new List<Room>(){new Room{RoomID=2,RoomName="dd"},new Room{RoomID=1,RoomName="qq"}}},
new HoteAvail{HotelID=3,Name="y",Rooms=new List<Room>(){new Room{RoomID=3,RoomName="yy"},new Room{RoomID=1,RoomName="rr"}}},
};
}
public static List<HoteAvail> GetAllII()
{
return new List<HoteAvail>()
{
new HoteAvail{HotelID=1,Name="Taj",Rooms=new List<Room>(){new Room{RoomID=1,RoomName="Deliux"},new Room{RoomID=1,RoomName="pp"}}},
new HoteAvail{HotelID=4,Name="x",Rooms=new List<Room>(){new Room{RoomID=2,RoomName="dd"},new Room{RoomID=1,RoomName="qq"}}},
new HoteAvail{HotelID=5,Name="y",Rooms=new List<Room>(){new Room{RoomID=3,RoomName="yy"},new Room{RoomID=1,RoomName="rr"}}},
};
}
}
I want to join the values of DAL.GetAll() and DAL.GetAllII() and result should contain only those values whose HotelID doesnot matches.
The final result set should be like :
new HoteAvail{HotelID=2,Name="x",Rooms=new List<Room>(){new Room{RoomID=2,RoomName="dd"},new Room{RoomID=1,RoomName="qq"}}},
new HoteAvail{HotelID=3,Name="y",Rooms=new List<Room>(){new Room{RoomID=3,RoomName="yy"},new Room{RoomID=1,RoomName="rr"}}},
new HoteAvail{HotelID=4,Name="x",Rooms=new List<Room>(){new Room{RoomID=2,RoomName="dd"},new Room{RoomID=1,RoomName="qq"}}},
new HoteAvail{HotelID=5,Name="y",Rooms=new List<Room>(){new Room{RoomID=3,RoomName="yy"},new Room{RoomID=1,RoomName="rr"}}}
I tried:
var groupBNames = new HashSet<string>(DAL.GetAll().Select(x => x.HotelID.ToString()));
var filteredEmployees = DAL.GetAllII().Where(x => !groupBNames.Contains(x.HotelID.ToString()));
var resultList = from a in DAL.GetAll()
where !(DAL.GetAllII().Any(HotelID => HotelID == a))
select a;
But I am not getting any success. Thanks in advance.
I'd recommend doing 2 excepts using a custom IEqualityComparer. You can use this method to create the comparer:
// create a comparer to compare HotelAvail objects by hotelId
// see http://www.codeducky.org/10-utilities-c-developers-should-know-part-two/
// for the implementation of EqualityComparers.Create, which is a nice shortcut
var comparer = EqualityComparers.Create<HoteAvail>(ha => ha.HotelId); // compare by hotelId
var results =
// first take all entries in GetAll() NOT IN GetAllII()
DAL.GetAll().Except(DAL.GetAllII(), comparer)
// then add all entries in GetAllII() NOT IN GetAll()
.Concat(DAL.GetAllII()).Except(DAL.GetAll(), comparer);
You could implement a custom IEqualityComparer<HoteAvail>:
public class HoteAvailComparer: IEqualityComparer<HoteAvail>
{
public bool Equals(HoteAvail x, HoteAvail y)
{
return x != null && y != null && x.HotelID == y.HotelID;
}
public int GetHashCode(HoteAvail obj)
{
return obj.HotelID;
}
}
that you can use for Enumerable.Except which is efficient since it's using a set:
var resultList = DAL.GetAll().Except(DAL.GetAllII(), new HoteAvailComparer());
Console.WriteLine(String.Join(",", resultList.Select(h => h.HotelID))); // 2,3
Update
It is giving me HotelId 2, 3 where as I want to join the values of
DAL.GetAll() and DAL.GetAllII() and result should contain only those
values whose HotelID doesnot matchesi.e. The result should have
HotelId 2,3,4,5
Then you need to use Except from both perspectives:
var hotelComparer = new HoteAvailComparer();
var all1 = DAL.GetAll();
var all2 = DAL.GetAllII();
var resultList = all1.Except(all2, hotelComparer).Concat(all2.Except(all1, hotelComparer));
The desired result 2,3,4,5:
Console.WriteLine(String.Join(",", resultList.Select(h => h.HotelID)));
Of course you could also use Concat and GroupBy, but it's less efficient and maintainable:
resultList = all1.Concat(all2).GroupBy(h => h.HotelID)
.Where(g => g.Count() == 1)
.SelectMany(g => g);
You can use the IEqualityComparer<HoteAvail> for many other LINQ methods like GroupBy+Distinct,Join,Intersect etc.

Merging two lists in linq without anonymous type

I have: var list = IEnumerable<MergedData> where
public class MergedData
{
public A A;
public B B;
}
public class A
{
public int Id;
public string Value;
}
public class B
{
public int Id;
public string Value;
}
what can I do such that I end up with a list of a merged type with A.Id, A.Value, B.Id, B.Value without creating an anonymous type?
I would like the merging procedure to be independent of the specific types A and B.
Even though I rather prefer the idea behind #Yumei De Armas answer. Just create a new class containing all the properties that you need and create new instances of this class. Something like:
public class MergedData
{
public int AId;
public string AValue;
public int BId;
public string BValue;
}
And in your LINQ you just use this class instead of anonymous object like :
..new MergedData { AId = some_value, AValue = some_value, BId = some_value, BValue = some_value}
However, you already have the two classes in your MergedData class so if you insist on your design then you should complicate the syntax a little more:
..new MergedData { new A { Id = some_value, Value = some value}, new B { Id = some_value, Value = some value}}
Why cannot you use anonymous type?
var result = list.Select(item => new {
AId = item.A.Id,
AValue = item.A.Value,
BId = item.B.Id,
BValue= item.B.Value});
What exactly would you need?
For example, the list with values A = {2, "2"} and B = {3,"3"}, A = {4, "4"} and B = {5,"5"}, what would be the expected result?
NOTE: If you want to have both in the same list, you could do something like this I think... (I haven't tested it)
var result = list.Select(item => new {
Id = item.A.Id,
Value = item.A.Value
}).ToList().AddRange(list.Select(item => new {
Id = item.B.Id,
Value = item.B.Value
});
However, you will still need to have the Id and Value in different fields. Do you also want both fields to be added in the same list?
I think you need to make your question more clear - especially regarding the expected output. Here's my interpretation:
public class MergedData<T,T2>
{
public T A;
public T2 B;
public MergedData(T A, T2 B)
{
this.A = A;
this.B = B;
}
}
public class A
{
public int Id;
public string Value;
}
public class B
{
public int Id;
public string Value;
}
var listOfMerged = new List<MergedData<A,B>>();
for (int i=0; i<listOfA.Count; i++)
listOfMerged.Add(new MergedData<A,B> (listOfA[i],listOfB[i]));
// Example of Use
var merged = listOfMerged[0];
Console.WriteLine(merged.A.Id);
Console.WriteLine(merged.B.Value);
If this is along the lines of what you want, I'd take a look at the Tuple type.

How can I group by a list of elements?

I run into this issue again and again: how can I group a list of objects by a containing list of other objects?
I have a list of objects of type A and each of these objects has an property (lets call it ListProp) which is a list also. ListProp has elements of the type B. There are multiple elements of type A with identically B-objects in ListProp, but the ListProp property reference differs from element to element. How can I group these A-objects the fastest way, where the B-objects in ListProp are identically?
Sample code:
class Program
{
static void Main(string[] args)
{
var exampleList = new List<A>
{
// Should be in first group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 1 }}
}},
// Should be in first group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 1 }}
}},
// Should be in second group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 1 }},
new B { Prop = new C { Number = 1 }}
}},
// Should be in third group
new A { ListProp = new List<B>
{
new B { Prop = new C { Number = 0 }},
new B { Prop = new C { Number = 0 }}
}}
};
// Doesn't work because the reference of ListProp is always different
var groupedExampleList = exampleList.GroupBy(x => x.ListProp);
}
}
class C
{
public int Number { get; set; }
public override bool Equals(object o)
{
if (o is C)
return Number.Equals(((C)o).Number);
else
return false;
}
}
class B
{
public C Prop { get; set; }
}
class A
{
public IList<B> ListProp { get; set; }
}
You can implement IEqualityComparer<List<B>> and use it in the other GroupBy overload.
public class ListOfBEqualityComparer : IEqualityComparer<List<B>>
{
public bool Equals(List<B> x, List<B> y)
{
// you can also implement IEqualityComparer<B> and use the overload
return x.SequenceEqual(y);
}
public int GetHashCode(List<B> obj)
{
//implementation of List<T> may not work for your situation
return obj.GetHashCode();
}
}
Then you can use the overload
var groupedExampleList = exampleList.GroupBy(x => x.ListProp,
new ListOfBEqualityComparer());
Try this:
GroupBy(x => String.Join(",", x.ListProp));
It will group by 0,1; 0,1; 0,1; 0,1,1; 0,1 accordingly.
I would approach this the following way:
Associate each child element (in ListProp property) with its parent
Group the parents by children
Project the results
var data = exampleList.SelectMany(a=>a.ListProp.Select(x=>new{Key = x.Prop.Number, Value = a}))
.GroupBy(x=>x.Key)
.Select(g=>new {Number = g.Key, Items = g.ToList()});

Categories