I have a list of emails grouped by hostname. I need to join these emails in an interleaved way and then send them.
public List<string> ReturnOrder()
{
List<string> listMerge = new List<string>();
List<List<string>> listEmail = new List<List<string>>();
listEmail.Add(new List<string> { "[email protected]", "[email protected]", "[email protected]" });
listEmail.Add(new List<string> { "[email protected]", "[email protected]" });
listEmail.Add(new List<string> { "[email protected]", "[email protected]", "[email protected]", "[email protected]"});
return listMerge;
}
//Order: [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected] [email protected]
I need to get the following result, I made several unsuccessful attempts
CodePudding user response:
A simplistic implementation would be something like this:
List<string> listMerge = new List<string>();
List<List<string>> listEmail = new List<List<string>>();
listEmail.Add(new List<string> { "[email protected]", "[email protected]", "[email protected]" });
listEmail.Add(new List<string> { "[email protected]", "[email protected]" });
listEmail.Add(new List<string> { "[email protected]", "[email protected]", "[email protected]", "[email protected]" });
for(int i =0; i < listEmail.Select(x => x.Count).Max(); i )
{
for(int j = 0; j < listEmail.Count; j )
{
if (listEmail[j].Count <= i)
continue;
listMerge.Add(listEmail[j][i]);
}
}
Each time the outer loop ends, one item from all lists in consumed. So, the outer loop runs for the number of iterations equal to longest list.
Inner loop loops through each list and picks one item (if one exists for the specified index.)
This can probably be made more efficient, but for your purposes, this much should be sufficient.
CodePudding user response:
are you trying to sort your list with the complex List<List> type?
If you want to sort a more complex list, like a more dimensional one, and return just the sorted elements as a simple list, you should always start to flatten your list to a one dimensional list.
public static List<string> FlattenComplexList(List<List<string>> complexList)
{
List<string> flattenList = new();
complexList.ForEach(entry => flattenList.AddRange(entry));
return flattenList;
}
After you have flatten your list, you can just use the sort function of c# to sort your one dimensional array.
CodePudding user response:
you can do select many from complex list and then to orderby
List<string> listMerge = new List<string>();
List<List<string>> listEmail = new List<List<string>>();
listEmail.Add(new List<string> { "[email protected]", "[email protected]", "[email protected]" });
listEmail.Add(new List<string> { "[email protected]", "[email protected]" });
listEmail.Add(new List<string> { "[email protected]", "[email protected]", "[email protected]", "[email protected]"});
listMerge=listEmail.SelectMany(i=>{
return i;
}).OrderBy(o=>{
return o;
}).ToList();
Console.WriteLine(JsonConvert.SerializeObject(listMerge));
CodePudding user response:
A crude method using Linq would be:
var sorted = listEmail.SelectMany( (el, io) => el.Select((e,ii) => new {inner=ii, outer=io, email=e}))
.OrderBy(x => x.inner)
.ThenBy(x => x.outer)
.Select(x => x.email);
Translation: Flatten the list of lists, grabbing the index of the item in each list and the index of the list. Then order first by the index of the item, then by the index of the list.
But, that's not terribly intuitive. I would prefer the explicit loop that R J suggests (and it will be faster since you don't have to "sort" the data when you're accessing the lists in order)
CodePudding user response:
When you have a List of Lists (or more generally, an IEnumerable
of IEnumerables
), you can flatten them using the LINQ method SelectMany()
:
List<string> listMerge = listEmail.SelectMany(l => l).ToList();
// "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]", "[email protected]"
Unfortunately, this will simply append the lists back-to-back, it will not interleave them as you requested.
Your unique requirement can be solved with a little cleverness by finding the largest list, implementing your own zip with null placeholders, and then flattening the result. A for loop would be clearest, but here's a quick stab using only LINQ for fun:
int longestList = listEmail.Max(list => list.Count);
List<string> listMerge = Enumerable.Range(0, longestList)
.Select(i => listEmail.Select(list => list.Skip(i).FirstOrDefault()))
.SelectMany(l => l)
.Where(email => email != null)
.ToList();
For your future information, there is another LINQ method that can be used to interleave two IEnumerables of the same length called Zip()
, but this is not useful today because you have 3 lists, and they are of a different lengths.
Final Solution:
List<List<string>> listEmail = new List<List<string>>();
listEmail.Add(new List<string>{"[email protected]", "[email protected]", "[email protected]"});
listEmail.Add(new List<string>{"[email protected]", "[email protected]"});
listEmail.Add(new List<string>{"[email protected]", "[email protected]", "[email protected]", "[email protected]"});
int longestList = listEmail.Max(list => list.Count);
List<string> listMerge = Enumerable.Range(0, longestList)
.Select(i => listEmail.Select(list => list.Skip(i).FirstOrDefault()))
.SelectMany(l => l).Where(email => email != null).ToList();
Console.WriteLine(String.Join(", ", listMerge));
You can test this out here:
Output: [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected], [email protected]