Elegant way to create a nested Dictionary in C# - c#

I realized that I didn't give enough information for most people to read my mind and understand all my needs, so I changed this somewhat from the original.
Say I've got a list of items of a class like this:
public class Thing
{
int Foo;
int Bar;
string Baz;
}
And I want to categorize the Baz string based on the values of Foo, then Bar. There will be at most one Thing for each possible combination of Foo and Bar values, but I'm not guaranteed to have a value for each one. It may help to conceptualize it as cell information for a table: Foo is the row number, Bar is the column number, and Baz is the value to be found there, but there won't necessarily be a value present for every cell.
IEnumerable<Thing> things = GetThings();
List<int> foos = GetAllFoos();
List<int> bars = GetAllBars();
Dictionary<int, Dictionary<int, string>> dict = // what do I put here?
foreach(int foo in foos)
{
// I may have code here to do something for each foo...
foreach(int bar in bars)
{
// I may have code here to do something for each bar...
if (dict.ContainsKey(foo) && dict[foo].ContainsKey(bar))
{
// I want to have O(1) lookups
string baz = dict[foo][bar];
// I may have code here to do something with the baz.
}
}
}
What's an easy, elegant way to generate the nested dictionary? I've been using C# long enough that I'm getting used to finding simple, one-line solutions for all of the common stuff like this, but this one has me stumped.

Here's a solution using Linq:
Dictionary<int, Dictionary<int, string>> dict = things
.GroupBy(thing => thing.Foo)
.ToDictionary(fooGroup => fooGroup.Key,
fooGroup => fooGroup.ToDictionary(thing => thing.Bar,
thing => thing.Baz));

An elegant way would be to not create the dictionaries yourself but use LINQ GroupBy and ToDictionary to generate it for you.
var things = new[] {
new Thing { Foo = 1, Bar = 2, Baz = "ONETWO!" },
new Thing { Foo = 1, Bar = 3, Baz = "ONETHREE!" },
new Thing { Foo = 1, Bar = 2, Baz = "ONETWO!" }
}.ToList();
var bazGroups = things
.GroupBy(t => t.Foo)
.ToDictionary(gFoo => gFoo.Key, gFoo => gFoo
.GroupBy(t => t.Bar)
.ToDictionary(gBar => gBar.Key, gBar => gBar.First().Baz));
Debug.Fail("Inspect the bazGroups variable.");
I assume that by categorizing Baz using Foo and Bar you mean that if two things have both Foo and Bar equals then their Baz value also be the same as well. Please correct me if I'm wrong.
You're basically group by the Foo property first...
then for each resulting group, you group on the Bar property...
then for each resulting group you take the first Baz value as the dictionary value.
If you noticed, the method names matched exactly what you are trying to do. :-)
EDIT: Here's another way using query comprehensions, they are longer but are quiet easier to read and grok:
var bazGroups =
(from t1 in things
group t1 by t1.Foo into gFoo
select new
{
Key = gFoo.Key,
Value = (from t2 in gFoo
group t2 by t2.Bar into gBar
select gBar)
.ToDictionary(g => g.Key, g => g.First().Baz)
})
.ToDictionary(g => g.Key, g => g.Value);
Unfortunately, there are no query comprehension counterpart for ToDictionary so it's not as elegant as the lambda expressions.
...
Hope this helps.

Define your own custom generic NestedDictionary class
public class NestedDictionary<K1, K2, V>:
Dictionary<K1, Dictionary<K2, V>> {}
then in your code you write
NestedDictionary<int, int, string> dict =
new NestedDictionary<int, int, string> ();
if you use the int, int, string one a lot, define a custom class for that too..
public class NestedIntStringDictionary:
NestedDictionary<int, int, string> {}
and then write:
NestedIntStringDictionary dict =
new NestedIntStringDictionary();
EDIT: To add capability to construct specific instance from provided List of items:
public class NestedIntStringDictionary:
NestedDictionary<int, int, string>
{
public NestedIntStringDictionary(IEnumerable<> items)
{
foreach(Thing t in items)
{
Dictionary<int, string> innrDict =
ContainsKey(t.Foo)? this[t.Foo]:
new Dictionary<int, string> ();
if (innrDict.ContainsKey(t.Bar))
throw new ArgumentException(
string.Format(
"key value: {0} is already in dictionary", t.Bar));
else innrDict.Add(t.Bar, t.Baz);
}
}
}
and then write:
NestedIntStringDictionary dict =
new NestedIntStringDictionary(GetThings());

Another approach would be to key your dictionary using an anonymous type based on both the Foo and Bar values.
var things = new List<Thing>
{
new Thing {Foo = 3, Bar = 4, Baz = "quick"},
new Thing {Foo = 3, Bar = 8, Baz = "brown"},
new Thing {Foo = 6, Bar = 4, Baz = "fox"},
new Thing {Foo = 6, Bar = 8, Baz = "jumps"}
};
var dict = things.ToDictionary(thing => new {thing.Foo, thing.Bar},
thing => thing.Baz);
var baz = dict[new {Foo = 3, Bar = 4}];
This effectively flattens your hierarchy into a single dictionary.
Note that this dictionary cannot be exposed externally since it is based on an anonymous type.
If the Foo and Bar value combination isn't unique in your original collection, then you would need to group them first.
var dict = things
.GroupBy(thing => new {thing.Foo, thing.Bar})
.ToDictionary(group => group.Key,
group => group.Select(thing => thing.Baz));
var bazes = dict[new {Foo = 3, Bar = 4}];
foreach (var baz in bazes)
{
//...
}

You may be able to use a KeyedCollection where you define:
class ThingCollection
: KeyedCollection<Dictionary<int,int>,Employee>
{
...
}

Use BeanMap's two key Map class. There is also a 3 key map, and it is quite extensible in case you need n keys.
http://beanmap.codeplex.com/
Your solution would then look like:
class Thing
{
public int Foo { get; set; }
public int Bar { get; set; }
public string Baz { get; set; }
}
[TestMethod]
public void ListToMapTest()
{
var things = new List<Thing>
{
new Thing {Foo = 3, Bar = 3, Baz = "quick"},
new Thing {Foo = 3, Bar = 4, Baz = "brown"},
new Thing {Foo = 6, Bar = 3, Baz = "fox"},
new Thing {Foo = 6, Bar = 4, Baz = "jumps"}
};
var thingMap = Map<int, int, string>.From(things, t => t.Foo, t => t.Bar, t => t.Baz);
Assert.IsTrue(thingMap.ContainsKey(3, 4));
Assert.AreEqual("brown", thingMap[3, 4]);
thingMap.DefaultValue = string.Empty;
Assert.AreEqual("brown", thingMap[3, 4]);
Assert.AreEqual(string.Empty, thingMap[3, 6]);
thingMap.DefaultGeneration = (k1, k2) => (k1.ToString() + k2.ToString());
Assert.IsFalse(thingMap.ContainsKey(3, 6));
Assert.AreEqual("36", thingMap[3, 6]);
Assert.IsTrue(thingMap.ContainsKey(3, 6));
}

I think the simplest approach would be to use the LINQ extension methods. Obviously I haven't tested this code for performace.
var items = new[] {
new Thing { Foo = 1, Bar = 3, Baz = "a" },
new Thing { Foo = 1, Bar = 3, Baz = "b" },
new Thing { Foo = 1, Bar = 4, Baz = "c" },
new Thing { Foo = 2, Bar = 4, Baz = "d" },
new Thing { Foo = 2, Bar = 5, Baz = "e" },
new Thing { Foo = 2, Bar = 5, Baz = "f" }
};
var q = items
.ToLookup(i => i.Foo) // first key
.ToDictionary(
i => i.Key,
i => i.ToLookup(
j => j.Bar, // second key
j => j.Baz)); // value
foreach (var foo in q) {
Console.WriteLine("{0}: ", foo.Key);
foreach (var bar in foo.Value) {
Console.WriteLine(" {0}: ", bar.Key);
foreach (var baz in bar) {
Console.WriteLine(" {0}", baz.ToUpper());
}
}
}
Console.ReadLine();
Output:
1:
3:
A
B
4:
C
2:
4:
D
5:
E
F

Dictionary<int, Dictionary<string, int>> nestedDictionary =
new Dictionary<int, Dictionary<string, int>>();

Related

c# how to create an array of an anonymous type? [duplicate]

In C# 3.0 you can create anonymous class with the following syntax
var o = new { Id = 1, Name = "Foo" };
Is there a way to add these anonymous class to a generic list?
Example:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<var> list = new List<var>();
list.Add(o);
list.Add(o1);
Another Example:
List<var> list = new List<var>();
while (....)
{
....
list.Add(new {Id = x, Name = y});
....
}
You could do:
var list = new[] { o, o1 }.ToList();
There are lots of ways of skinning this cat, but basically they'll all use type inference somewhere - which means you've got to be calling a generic method (possibly as an extension method). Another example might be:
public static List<T> CreateList<T>(params T[] elements)
{
return new List<T>(elements);
}
var list = CreateList(o, o1);
You get the idea :)
Here is the answer.
string result = String.Empty;
var list = new[]
{
new { Number = 10, Name = "Smith" },
new { Number = 10, Name = "John" }
}.ToList();
foreach (var item in list)
{
result += String.Format("Name={0}, Number={1}\n", item.Name, item.Number);
}
MessageBox.Show(result);
There are many ways to do this, but some of the responses here are creating a list that contains garbage elements, which requires you to clear the list.
If you are looking for an empty list of the generic type, use a Select against a List of Tuples to make the empty list. No elements will be instantiated.
Here's the one-liner to create an empty list:
var emptyList = new List<Tuple<int, string>>()
.Select(t => new { Id = t.Item1, Name = t.Item2 }).ToList();
Then you can add to it using your generic type:
emptyList.Add(new { Id = 1, Name = "foo" });
emptyList.Add(new { Id = 2, Name = "bar" });
As an alternative, you can do something like below to create the empty list (But, I prefer the first example because you can use it for a populated collection of Tuples as well) :
var emptyList = new List<object>()
.Select(t => new { Id = default(int), Name = default(string) }).ToList();
Not exactly, but you can say List<object> and things will work. However, list[0].Id won't work.
This will work at runtime in C# 4.0 by having a List<dynamic>, that is you won't get IntelliSense.
If you are using C# 7 or above, you can use tuple types instead of anonymous types.
var myList = new List<(int IntProp, string StrProp)>();
myList.Add((IntProp: 123, StrProp: "XYZ"));
I guess
List<T> CreateEmptyGenericList<T>(T example) {
return new List<T>();
}
void something() {
var o = new { Id = 1, Name = "foo" };
var emptyListOfAnonymousType = CreateEmptyGenericList(o);
}
will work.
You might also consider writing it like this:
void something() {
var String = string.Emtpy;
var Integer = int.MinValue;
var emptyListOfAnonymousType = CreateEmptyGenericList(new { Id = Integer, Name = String });
}
I usually use the following; mainly because you then "start" with a list that's empty.
var list = Enumerable.Range(0, 0).Select(e => new { ID = 1, Name = ""}).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
//etc.
Lately, I've been writing it like this instead:
var list = Enumerable.Repeat(new { ID = 1, Name = "" }, 0).ToList();
list.Add(new {ID = 753159, Name = "Lamont Cranston"} );
Using the repeat method would also allow you to do:
var myObj = new { ID = 1, Name = "John" };
var list = Enumerable.Repeat(myObj, 1).ToList();
list.Add(new { ID = 2, Name = "Liana" });
..which gives you the initial list with the first item already added.
You can do this in your code.
var list = new[] { new { Id = 1, Name = "Foo" } }.ToList();
list.Add(new { Id = 2, Name = "Bar" });
I checked the IL on several answers. This code efficiently provides an empty List:
using System.Linq;
…
var list = new[]{new{Id = default(int), Name = default(string)}}.Skip(1).ToList();
In latest version 4.0, can use dynamic like below
var list = new List<dynamic>();
list.Add(new {
Name = "Damith"
});
foreach(var item in list){
Console.WriteLine(item.Name);
}
}
You can create a list of dynamic.
List<dynamic> anons=new List<dynamic>();
foreach (Model model in models)
{
var anon= new
{
Id = model.Id,
Name=model.Name
};
anons.Add(anon);
}
"dynamic" gets initialized by the first value added.
Here is a another method of creating a List of anonymous types that allows you to start with an empty list, but still have access to IntelliSense.
var items = "".Select( t => new {Id = 1, Name = "foo"} ).ToList();
If you wanted to keep the first item, just put one letter in the string.
var items = "1".Select( t => new {Id = 1, Name = "foo"} ).ToList();
Here is my attempt.
List<object> list = new List<object> { new { Id = 10, Name = "Testing1" }, new {Id =2, Name ="Testing2" }};
I came up with this when I wrote something similar for making a Anonymous List for a custom type.
I'm very surprised nobody has suggested collection initializers. This way can only add objects when the list is created hence the name however it seems like the nicest way of doing it. No need to create an array then convert it to a list.
var list = new List<dynamic>()
{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
};
You can always use object instead of dynamic but trying to keep it in a true generic way then dynamic makes more sense.
Instead of this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List <var> list = new List<var>();
list.Add(o);
list.Add(o1);
You could do this:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
List<object> list = new List<object>();
list.Add(o);
list.Add(o1);
However, you will get a compiletime error if you try to do something like this in another scope, although it works at runtime:
private List<object> GetList()
{
List<object> list = new List<object>();
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
list.Add(o);
list.Add(o1);
return list;
}
private void WriteList()
{
foreach (var item in GetList())
{
Console.WriteLine("Name={0}{1}", item.Name, Environment.NewLine);
}
}
The problem is that only the members of Object are available at runtime, although intellisense will show the properties id and name.
In .net 4.0 a solution is to use the keyword dynamic istead of object in the code above.
Another solution is to use reflection to get the properties
using System;
using System.Collections.Generic;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
var anonymous = p.GetList(new[]{
new { Id = 1, Name = "Foo" },
new { Id = 2, Name = "Bar" }
});
p.WriteList(anonymous);
}
private List<T> GetList<T>(params T[] elements)
{
var a = TypeGenerator(elements);
return a;
}
public static List<T> TypeGenerator<T>(T[] at)
{
return new List<T>(at);
}
private void WriteList<T>(List<T> elements)
{
PropertyInfo[] pi = typeof(T).GetProperties();
foreach (var el in elements)
{
foreach (var p in pi)
{
Console.WriteLine("{0}", p.GetValue(el, null));
}
}
Console.ReadLine();
}
}
}
You can do it this way:
var o = new { Id = 1, Name = "Foo" };
var o1 = new { Id = 2, Name = "Bar" };
var array = new[] { o, o1 };
var list = array.ToList();
list.Add(new { Id = 3, Name = "Yeah" });
It seems a little "hacky" to me, but it works - if you really need to have a list and can't just use the anonymous array.
This is an old question, but I thought I'd put in my C# 6 answer. I often have to set up test data that is easily entered in-code as a list of tuples. With a couple of extension functions, it is possible to have this nice, compact format, without repeating the names on each entry.
var people= new List<Tuple<int, int, string>>() {
{1, 11, "Adam"},
{2, 22, "Bill"},
{3, 33, "Carol"}
}.Select(t => new { Id = t.Item1, Age = t.Item2, Name = t.Item3 });
This gives an IEnumerable - if you want a list that you can add to then just add ToList().
The magic comes from custom extension Add methods for tuples, as described at https://stackoverflow.com/a/27455822/4536527.
public static class TupleListExtensions {
public static void Add<T1, T2>(this IList<Tuple<T1, T2>> list,
T1 item1, T2 item2) {
list.Add(Tuple.Create(item1, item2));
}
public static void Add<T1, T2, T3>(this IList<Tuple<T1, T2, T3>> list,
T1 item1, T2 item2, T3 item3) {
list.Add(Tuple.Create(item1, item2, item3));
}
// and so on...
}
The only thing I don't like is that the types are separated from the names, but if you really don't want to make a new class then this approach will still let you have readable data.
var list = new[]{
new{
FirstField = default(string),
SecondField = default(int),
ThirdField = default(double)
}
}.ToList();
list.RemoveAt(0);
For your second example, where you have to initialize a new List<T>, one idea is to create an anonymous list, and then clear it.
var list = new[] { o, o1 }.ToList();
list.Clear();
//and you can keep adding.
while (....)
{
....
list.Add(new { Id = x, Name = y });
....
}
Or as an extension method, should be easier:
public static List<T> GetEmptyListOfThisType<T>(this T item)
{
return new List<T>();
}
//so you can call:
var list = new { Id = 0, Name = "" }.GetEmptyListOfThisType();
Or probably even shorter,
var list = new int[0].Select(x => new { Id = 0, Name = "" }).Tolist();
Deriving from this answer, I came up with two methods that could do the task:
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't called, it is only used
/// for the needed type inference. This overload is for when you don't have an instance of the anon class
/// and don't want to make one to make the list.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(Func<T> definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
/// <summary>
/// Create a list of the given anonymous class. <paramref name="definition"/> isn't added to the list, it is
/// only used for the needed type inference. This overload is for when you do have an instance of the anon
/// class and don't want the compiler to waste time making a temp class to define the type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="definition"></param>
/// <returns></returns>
#pragma warning disable RECS0154 // Parameter is never used
public static List<T> CreateListOfAnonType<T>(T definition)
#pragma warning restore RECS0154 // Parameter is never used
{
return new List<T>();
}
You can use the methods like
var emptyList = CreateListOfAnonType(()=>new { Id = default(int), Name = default(string) });
//or
var existingAnonInstance = new { Id = 59, Name = "Joe" };
var otherEmptyList = CreateListOfAnonType(existingAnonInstance);
This answer has a similar idea, but I didn't see it until after I made those methods.
Using Reflection
Microsoft documentation about this topic.
using System;
using System.Collections;
using System.Collections.Generic;
var anonObj = new { Id = 1, Name = "Foo" };
var anonType = anonObj.GetType();
var listType = typeof(List<>);
// We know that List<> have only one generic argument, so we do this:
var contructed = listType.MakeGenericType(anonType);
// Create instance
var instance = Activator.CreateInstance(contructed);
// Using it
var list = (IList)instance;
list.Add(anonObj);
For Dictionary<,> you need to pass 2 arguments
Ex.: dicType.MakeGenericType( type1, type2 )
And for generic types with constraints (where T : struct),
we need to do more verifications. Check microsoft docs to learn how.
Try with this:
var result = new List<object>();
foreach (var test in model.ToList()) {
result.Add(new {Id = test.IdSoc,Nom = test.Nom});
}

How to use dictionary in c# to compare two lists

Currently, I have implemented two lists with a double for loop to find matches between the two lists so I can join on them.
I have a list A which contains an ID and some other columns. I have a list B which contains an ID and some other columns. I have currently implemented a for loop within a for loop in order to make the comparisons for all the IDs so that I can find the ones that match and then return the joined results. I know want to understand how to implement a dictionary in this case as that will be more efficient to fix this problem.
public IEnumerable<Details> GetDetails(string ID)
{
// there are two lists defined up here
for (var item in listA)
{
for (var item2 in listB)
{
if (item.ID == item2.ID)
{
item.Name = item2.name;
}
}
}
return results;
}
Instead of having this double for loop, which is very inefficient. I want to learn how to implement a dictionary to fix this problem.
The dictionary would use the ids as keys (or indexes) so
Dictionary<string, object> myListA = new Dictionary<string, object>();
Dictionary<string, object> myListB = new Dictionary<string, object>();
public object GetDetails(string ID)
{
object a = myListA[ID];
object b = myListB[ID];
// combine them here how you want
// object c = a + b;
return c;
}
How about using linq to achieve your actual requirement? Something like:
public IEnumerable<A> GetDetails(int ID)
{
var listA = new List<A>
{
new A(){ ID = 1, Name = 2 },
new A(){ ID = 3, Name = 4 },
new A(){ ID = 5, Name = 6 },
};
var listB = new List<B>
{
new B(){ X = 1, name = 0 },
new B(){ X = 3, name = 1 }
};
return listA.Join(listB, k => k.ID, k => k.ID, (item, item2) =>
{
item.Name = item2.name;
return item;
}).Where(w => w.ID == ID);
}
If you just want the common IDs in the two lists, you can achieve that like this:
var commonIds = listA.Select(o => o.ID).Intersect(listB.Select(o => o.ID));

Build a Where clause using a loop and concatening each iterarion with an OR

I have a list of N pairs of integers, e.g.:
2, 4
5, 7
9, 10
11, 12
And I need to build a query like:
WHERE
(foo = 2 AND bar = 4) OR
(foo = 5 AND bar = 7) OR
(foo = 9 AND bar = 10) OR
(foo = 11 AND bar = 12)
If it was a constant length list, I could write something like:
var query = myClass.Where(x =>
(foo == values[0][0] && bar == values[0][1]) ||
(foo == values[1][0] && bar == values[1][1]) ||
(foo == values[2][0] && bar == values[2][1]) ||
(foo == values[3][0] && bar == values[3][1]));
But the length of the list varies, and I am looking for a way to create the query using a loop.
I found I can use Queryable.Union() for a similar result, but considering there are more conditions in the query, and the list of pairs can be long, I would prefer to avoid the union.
Is there any solution for this problem?
You can perform one trick - concatenate looking for fields: foo and bar and then use Contains method:
var filters = new int[][] {
new int[] { 2, 4 },
new int[] { 5, 7 },
new int[] { 9, 10 },
new int[] { 11, 12 }
};
var newFilter = filters.Select(x => x[0] + "-" + x[1]).ToList();
var answer = dbContext.myClass.Where(x => newFilter.Contains(x.foo + "-" + x.bar)).ToList();
Assuming that values is a jagged array, and that myClass is an IEnumerable<T> of an object that has foo and bar properties:
var query = myClass.Where(x => values.Any(y => x.foo == y[0] && x.bar == y[1]));
The inner Any statement, which is run against each object in myClass, looks for any "row" in values whose contents matches the foo and bar properties of myClass. In essence, the Any clause iterates over each row in the table, while the Where clause iterates over (and filters) each object in myClass.
However, I don't know that it will be any more efficient than using a Union.
As noted in the comments, this method doesn't work with LINQ to Entities. This could still be used in conjunction with Entity Framework by pulling all of the records from the database and filtering them in memory, but obviously this is not an efficient solution.
try this:
var query = myClass.Where(x => x.Any(p => p[0] == foo && p[1] == bar));
Take your integer set and make it into a map. Then test for .Any using LINQ.
var map = new Dictionary<int, int>()
{
{2, 4},
{5, 7},
{9, 10},
{11, 12}
};
var foo = 2;
var bar = 4;
var q = map.Any(kv => foo == kv.Key && bar == kv.Value);
Alternatively, you can take the list of int pairs and make them into a list of Tuple<int, int> and test for foo and bar like this:
var q = listOfTuples.Any(tp => foo == tp.Item1 && bar == tp.Item2);
Main point here is that you need to take your "list of pairs of ints" and make a decision on how that information will be structured. Once you make that call, everything else falls into place. One step at a time, right? :-)
I found a solution for this problem based on a similar solution described at Dynamic Queries in Entity Framework using Expression Trees
Here is the solution.
First, a class to hold a foo and bar pair:
public class FooBarPair
{
public int Foo { get; set; }
public int Bar { get; set; }
}
Then, the collection of foos and bars:
var pairs = new FooBarPair[]
{
new Foo() { Foo = 10537, Bar = 1034 },
new Foo() { Foo = 999, Bar = 999 },
new Foo() { Foo = 888, Bar = 888 },
new Foo() { Foo = 10586, Bar = 63 },
};
And here is the code that builds the query expression:
public static void Main()
{
Expression<Func<MyClass, bool>> whereClause =
BuildOrExpressionTree<MyClass, int>(pairs, m => m.Foo + m.Bar);
var myClasss = model.Set<MyClass>();
IQueryable<MyClass> query = myClasss.Where(whereClause);
}
/// <summary>
/// Starts a recursion to build WHERE (m.Foo = X1 AND m.Bar = Y1) [OR (m.Foo = X2 AND m.Bar = Y2) [...]].
/// </summary>
private static Expression<Func<TValue, bool>> BuildOrExpressionTree<TValue, TCompareAgainst>(
IEnumerable<FooBarPair> wantedItems,
Expression<Func<TValue, TCompareAgainst>> convertBetweenTypes1)
{
ParameterExpression inputParam1 = convertBetweenTypes1.Parameters[0];
BinaryExpression binaryExpression = convertBetweenTypes1.Body as BinaryExpression;
Expression binaryExpressionTree = BuildBinaryOrTree<FooBarPair>(
wantedItems.GetEnumerator(),
binaryExpression.Left,
binaryExpression.Right,
null);
return Expression.Lambda<Func<TValue, bool>>(binaryExpressionTree, new[] { inputParam1 });
}
/// <summary>
/// Recursive function to append one 'OR (m.Foo = X AND m.Bar = Y)' expression.
/// </summary>
private static Expression BuildBinaryOrTree<T>(
IEnumerator<FooBarPair> itemEnumerator,
Expression expressionToCompareTo1,
Expression expressionToCompareTo2,
Expression prevExpression)
{
if (itemEnumerator.MoveNext() == false)
{
return prevExpression;
}
ConstantExpression fooConstant = Expression.Constant(itemEnumerator.Current.Foo, typeof(int));
ConstantExpression barConstant = Expression.Constant(itemEnumerator.Current.Bar, typeof(int));
BinaryExpression fooComparison = Expression.Equal(expressionToCompareTo1, fooConstant);
BinaryExpression barComparison = Expression.Equal(expressionToCompareTo2, barConstant);
BinaryExpression newExpression = Expression.AndAlso(fooComparison, barComparison);
if (prevExpression != null)
{
newExpression = Expression.OrElse(prevExpression, newExpression);
}
return BuildBinaryOrTree<FooBarPair>(
itemEnumerator,
expressionToCompareTo1,
expressionToCompareTo2,
newExpression);
}
Thanks everybody!

c# Merging 3 collection list into one list

I have 3 collection list as below.
public static List<Thing> English = new List<Thing>
{
new Thing {ID = 1, Stuff = "one"},
new Thing {ID = 2, Stuff = "two"},
new Thing {ID = 3, Stuff = "three"}
};
public static List<Thing> Spanish = new List<Thing>
{
new Thing {ID = 1, Stuff = "uno"},
new Thing {ID = 2, Stuff = "dos"},
new Thing {ID = 3, Stuff = "tres"},
new Thing {ID = 4, Stuff = "cuatro"}
};
public static List<Thing> German = new List<Thing>
{
new Thing {ID = 1, Stuff = "eins"},
new Thing {ID = 2, Stuff = "zwei"},
new Thing {ID = 3, Stuff = "drei"}
};
During runtime, the length of the list may vary. For eg, German may take 5 values, english with 2 and spanish with one.
I need to find which list has the max value and need to get the output in the below format.
Id English German Spanish
1 one eins uno
2 two zwei dos
3 three drei tres
4 cuatro
Can you please help me to solve this.
Try this:
English.Select(t => new Tuple<Thing,int>(t, 1)).Concatenate(
German.Select(t => new Tuple<Thing,int>(t, 2)).Concatenate(
Spanish.Select(t => new Tuple<Thing,int>(t, 3))
)
).GroupBy(p => p.Item1.ID)
.Select(g => new {
Id = g.Key
, English = g.Where(t => t.Item2==1).Select(t => t.Item2.Stuff).SingleOrDefault()
, German = g.Where(t => t.Item2==2).Select(t => t.Item2.Stuff).SingleOrDefault()
, Spanish = g.Where(t => t.Item2==3).Select(t => t.Item2.Stuff).SingleOrDefault()
});
The idea is to tag the original items with their collection origin (1 for English, 2 for German, 3 for Spanish), group them by ID, and then pull the details for individual languages using the tag that we added in the first step.
If they all start at one and never skip any numbers (but can end at any point) then you can use a more simple approach, such as this:
int count = Math.Max(English.Count, Math.Max(Spanish.Count, German.Count));
var query = Enumerable.Range(0, count)
.Select(num => new
{
Id = num + 1,
English = GetValue(English, num),
Spanish = GetValue(Spanish, num),
German = GetValue(German, num),
});
If it's possible for numbers to be skipped, or not start at one, then you could use this slightly more complex approach:
var englishDic = English.ToDictionary(thing => thing.ID, thing => thing.Stuff);
var spanishDic = Spanish.ToDictionary(thing => thing.ID, thing => thing.Stuff);
var germanDic = German.ToDictionary(thing => thing.ID, thing => thing.Stuff);
var query = englishDic.Keys
.Union(spanishDic.Keys)
.Union(germanDic.Keys)
.Select(key => new
{
Id = key,
English = GetValue(englishDic, key),
Spanish = GetValue(spanishDic, key),
German = GetValue(germanDic, key),
});
A few helper functions were needed to avoid invalid argument errors:
public static string GetValue(Dictionary<int, string> dictionary, int key)
{
string output;
if (dictionary.TryGetValue(key, out output))
return output;
else
return "";
}
public static string GetValue(List<Thing> list, int index)
{
if (index < list.Count)
return list[index].Stuff;
else
return "";
}
This was fun :)
I did this, which works, but, like many of these answers, isn't very efficient:
public IEnumerable ListEmAll() {
return new List<int>() // just for balance, start with empty list
.Union( English.Select(o => o.ID) )
.Union( Spanish.Select(o => o.ID) )
.Union( German.Select(o => o.ID) )
.OrderBy(id => id)
.Select(id =>
new
{
ID = id,
English = English.Where(o => o.ID == id).Select(o => o.Stuff),
Spanish = Spanish.Where(o => o.ID == id).Select(o => o.Stuff),
German = German.Where(o => o.ID == id).Select(o => o.Stuff)
});
}
But what I like better is to not use Linq, and return a compound dictionary... no expensive lookups on the lists.
// keep a list of the languages for later
static Dictionary<string, List<Thing>> languages = new Dictionary<string, List<Thing>>(){
{"English", English},
{"Spanish", Spanish},
{"German", German}
};
// result[3]["English"] = "three"
public Dictionary<int, Dictionary<string, string>> ListEmAll_better() {
Dictionary<int, Dictionary<string, string>> result = new Dictionary<int, Dictionary<string, string>>();
foreach(var lang in languages.Keys) {
foreach(var thing in languages[lang]) {
if(!result.ContainsKey(thing.ID)) {
result[thing.ID] = new Dictionary<string, string>();
}
result[thing.ID][lang] = thing.Stuff;
}
}
return result;
}

How do I merge records using LINQ?

I'd like to merge two records using a condition for each column in the row. I'd give you a code sample but I don't know where to start.
class Foo
{
public int i {get;set;}
public int b{get;set;}
public string first{get;set;}
public string last{get;set;}
}
//...
var list = new List<Foo>() {
new Foo () { i=1, b=0, first="Vince", last="P"},
new Foo () { i=1, b=1, first="Vince", last="P"},
new Foo () { i=1, b=0, first="Bob", last="Z"},
new Foo () { i=0, b=1, first="Bob", last="Z"},
} ;
// This is how I'd like my result to look like
// Record 1 - i = 1, b = 1, first="Vince", last = "P"
// Record 2 - i = 1, b = 1, first="Bob", last = "Z"
You can group the result, then aggregate the fields from the items in the group:
var result = list.GroupBy(f => f.first).Select(
g => new Foo() {
b = g.Aggregate(0, (a, f) => a | f.b),
i = g.Aggregate(0, (a, f) => a | f.i),
first = g.Key,
last = g.First().last
}
);
You could use the Aggregate method in LINQ.
First add a method to Foo, say Merge that returns a new Foo based on your merging rules.
public Foo Merge (Foo other)
{
// Implement merge rules here ...
return new Foo {..., b=Math.Max(this.b, other,b), ...};
}
You could also, instead, create a helper method outside the Foo class that does the merging.
Now use Aggregate over your list, using the first element as the seed, merging each record with the current aggregate value as you go. Or, instead of using Aggregate (since it's a somewhat contrived use of LINQ in this case), just do:
Foo result = list.First();
foreach (var item in list.Skip(1)) result = result.Merge(item);
How are your merge rules specified?
I found a non-elegant solution that works
var result = list.GroupBy(i=>i.first);
foreach (IGrouping<string, Foo> grp in result)
{
grp.Aggregate ((f1, f2) => {
return new Foo() {
b = f1.b | f2.b,
i = f1.i | f2.i,
first = f1.first,
last = f1.last
};
});
}

Categories