Reputation: 67
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.
Upvotes: 4
Views: 277
Reputation: 397
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;
}
Upvotes: 0
Reputation: 203827
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 "";
}
Upvotes: 1
Reputation: 726809
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.
Upvotes: 6