Let's say I have a list of unknown number of elements in string value, I want to divide it to n subarray or lists (n could be any int, for example n=3), what is best way to do it?
note: the number of elements in each group is not necessary to be equal
LINQ GroupBy and Select methods can help:
var list = new List<string>() { "A", "B", "C", "D", "E", "F", "G" };
int groupCount = 3;
var subLists = list.Select((s, i) => new {Str = s, Index = i}).
GroupBy(o => o.Index % groupCount, o => o.Str).
Select(coll => coll.ToList()).
ToList();
This code will result in subLists containing a list of three List<string> collections: {"A", "D", "G"}, {"B", "E"} and {"C", "F"}. In order to achieve that I based my grouping on element indices in the original list (there is an overload for Select method that lets you do that, see link above). You can use some other logic to select the key.
In my example subLists is a List<List<string>>. If you need an array, use ToArray where appropriate.
EDIT: using modulo operation for grouping may not be a good idea if you care about the way values are distributed between lists. Probably the better option is to do it this way:
var list = new List<string>() { "A", "B", "C", "D", "E", "F", "G" };
int groupCount = 3;
int maxPerGroup = (int)Math.Ceiling((double)list.Count / groupCount);
var subLists = list.Select((s, i) => new {Str = s, Index = i}).
GroupBy(o => o.Index / maxPerGroup, o => o.Str).
Select(coll => coll.ToList()).
ToList();
This will produce the following result: {"A", "B", "C"}, {"D", "E", "F"}, {"G"} which may be more sane way to distribute the values.
Bottom line is, you can achieve what you need by using GroupBy and Select methods, just provide the correct grouping logic that is suitable for your domain.
alternately, you can do it in linq like
var n = 4;
var i = 0;
var list = new List<string> { "a", "b", "c", "d", "e", "f", "g" };
var res = list.GroupBy(x => Math.Ceiling((double)++i / n)).Select(x=>x.Select(y=>y).ToList()).ToList();
Console.Write(res);
write this in Console program or Linqpad and change values of n to see the effect
Related
This question already has answers here:
How do I Transpose a multi dimensional array?
(2 answers)
Closed 2 years ago.
I want to rearrange a nested list, group each items of the same index from list into a list.
Here is what I want to achieve.
INPUT
{
["a", "b", "c"],
["m", "n", "o"],
["x", "y", "z"],
}
OUTPUT
{
["a", "m", "x"],
["b", "n", "y"],
["c", "o", "z"],
}
How do I achieve this using C#?
You can use selector with index and GroupBy:
var x = new[]{new[]{"a", "b", "c"}, new[]{"m", "n", "o"}, new[]{"x", "y", "z"}, };
var arrs = x
.SelectMany(arr => arr.Select((inner, i) => (inner, i))) // project inner elements with index & flatten
.GroupBy(i => i.i) // group by element index
.Select(g => g.Select(gi => gi.inner).ToArray()) // select element from projection
.ToArray();
The operation you want to perform is called transposition.
Below solution assumes that all arrays inside are of the same length:
var listOfArrays = new List<string[]>(){
new string[]{"a", "b", "c"},
new string[]{"m", "n", "o"},
new string[]{"x", "y", "z"},
};
var transposed = new List<string[]>();
var internalSize = listOfArrays[0].Length;
for(int i = 0; i < internalSize; i++)
{
var column = new List<string>();
foreach(var array in listOfArrays)
{
column.Add(array[i]);
}
transposed.Add(column.ToArray());
}
My English isn't that good.
I try to make a TextLogger, for example:
if I have two different arrays:
string[] array1 = {"a", "b", "c", "d"}
string[] array2 = {"y", "c", "h", "f"}
and I have the char "c" in both of the arrays, then both of the char "c" should be removed.
Output:
a, b, d, h, y, f
this is what I managed to do so far:
string[] array1 = {"a", "b", "c", "d"}
string[] array2 = {"y", "c", "h", "f"}
for(int i = 0; i < array1.Length; i++)
{
if(array1[i] == array2[i])
{
}
}
edit(sorry for keep changing my question):
and how I can do it with this:
ArrayList array1 = new ArrayList();
array1.Add("a");
array1.Add("b");
array1.Add("c");
array1.Add("d");
ArrayList array2 = new ArrayList();
array1.Add("y");
array1.Add("c");
array1.Add("h");
array1.Add("f");
1- Get common items using Enumerable.Intersect
2- replace each array by the same array except common items using Enumerable.Except
string[] array1 = { "a", "b", "c", "d" };
string[] array2 = { "y", "c", "h", "f" };
var intersect = array1.Intersect(array2); // 1
array1 = array1.Except(intersect).ToArray(); //2
array2 = array2.Except(intersect).ToArray(); //2
Edit: to take into account double values as mentioned in the comment:
string[] array1 = { "a", "b", "b", "b", "c", "d" };
string[] array2 = { "y", "b", "c", "h", "f" };
var grpArray1 = array1.GroupBy(a => a)
.Select(grp => new { item = grp.Key, count = grp.Count() });
var grpArray2 = array2.GroupBy(a => a)
.Select(grp => new { item = grp.Key, count = grp.Count() });
array1 = grpArray1.Select(a =>
{
var bCount = array2.Count(x => x.Equals(a.item));
return new { item = a.item, finalCount = a.count - bCount };
})
.Where(a => a.finalCount > 0)
.SelectMany(a => Enumerable.Repeat(a.item, a.finalCount))
.ToArray();
array2 = grpArray2.Select(a =>
{
var bCount = array1.Count(x => x.Equals(a.item));
return new { item = a.item, finalCount = a.count - bCount };
})
.Where(a => a.finalCount > 0)
.SelectMany(a => Enumerable.Repeat(a.item, a.finalCount))
.ToArray();
Console.WriteLine("-->array1:");
foreach (var item in array1)
Console.WriteLine(item);
Console.WriteLine("-->array2:");
foreach (var item in array2)
Console.WriteLine(item);
The results:
-->array1:
a
b
b
d
-->array2:
y
h
f
If if understand your problem correctly, you don't need to change that arrays (array1 and array2) but get a result from both of them.
so, you can solve your problem using the GroupBy method
string[] array1 = { "a", "b", "c", "d" };
string[] array2 = { "y", "c", "h", "f" };
var filteredArray = array1.Concat(array2).GroupBy(x => x).Where(x => x.Count() == 1).Select(x=>x.Key);
Console.WriteLine(string.Join(" ", filteredArray));
Console.ReadLine();
what we can see here, is concat the arrays into 1 list, then group by the chars into groups, and then filter the groups that contain more than 1 element, and in the end revert it into list of chars (instead of list of groups)
Edit:
out of the comment about the duplicated "b" inside each of the arrays, i created a new (with little bit more complexity) that works for your case:
string[] array1 = { "a", "b", "b", "c", "d" };
string[] array2 = { "y", "c", "h", "f" };
var filteredArray = array1.GroupBy(x => x)
.Concat(array2.GroupBy(x => x))
.GroupBy(x => x.Key)
.Where(x => x.Count() == 1).SelectMany(x => x.Key);
Console.WriteLine(string.Join(" ", filteredArray));
Console.ReadLine();
whats happen there? we group the arrays each for himself, then concat the groups together, and then we group the groups by their keys, and the filter where each group contain more then 1 inner group, and in the end we select the groups keys (in addition it's promise us there is only 1 instance of each char)
Hope that helps!
The following code snippet should provide you a clear insight about how to perform your task:
String[] array1 = new String[] {"a", "b", "c", "d"};
String[] array2 = new String[] {"y", "c", "h", "f"};
String[] n1 = array1.Where(x => !array2.Contains(x)).ToArray();
String[] n2 = array2.Where(x => !array1.Contains(x)).ToArray();
Console.WriteLine("Array 1");
foreach (String s in n1)
Console.WriteLine(s);
Console.WriteLine("\nArray 2");
foreach (String s in n2)
Console.WriteLine(s);
Console.ReadLine();
The output is:
Array 1
a
b
d
Array 2
y
h
f
and can see a working demo by visiting this link. For more information concerning the methods I used in order to accomplish, visit the following links:
Enumerable.Contains
Enumerable.ToArray
Enumerable.Where
I have a list of strings List{"X", "W", "C", "A", "D", "B" } and I have another list of strings List{"A", "B", "C", "D"} that tells how the first list must be ordered. But the second list has only four items in it. I would like my first list to be ordered like this:
A, B, C, D, X, W. Actually the last two letters X and W doesn't matter how they are ordered, but they should be at the end of the list.
I tried this:
var newList = list1.OrderBy(i=>list2.IndexOf(i));
but this gives me only four items.
Your current code will give you 6 items. However, it will put X and W in the beginning since they have an index of -1 in list 2.
Here is how to fix that:
var list1 = new List<string> {"X", "W", "C", "A", "D", "B"};
var list2 = new List<string> {"A", "B", "C", "D"};
var newList = list1.OrderBy(x =>
{
var index = list2.IndexOf(x);
if (index == -1)
index = Int32.MaxValue;
return index;
})
.ToList();
One more way along with others.
List<string> list1 = new List<string>() {"X", "W", "C", "A", "D", "B" } ;
List<string> list2 = new List<string>() { "A", "B", "C", "D" } ;
var newList = list2.Intersect(list1)
.Union(list1.Except(list2));
Check Demo
This should work:
var newList = list1.OrderBy(i => {
var x = list2.IndexOf(i);
if(x == -1)
return int.MaxValue;
return x; });
Result (from LinqPad):
I have a SortedList of Lists and I am interested in finding the KEY that corresponds to the longest list (list with the most items in it). In code, that looks like:
// how the list is defined:
var myList = new SortedList<long, List<string>>();
// EXAMPLE data only:
myList.Add(0, new List<string>());
myList[0].AddRange(new []{"a", "b", "c"});
myList.Add(8, new List<string>());
myList[8].AddRange(new []{"1", "2"});
myList.Add(23, new List<string>());
myList[23].AddRange(new []{"c", "d", "e", "f", "g"});
In the above example the result should be "23" since that is the key that goes with the longest list.
I know how to write this with a for loop, but I think this should be a simple to do with LINQ. That said, I can't seem to get the syntax quite right! Any help is appreciated!
There's maybe a more efficient way, but you can order by count (of value) descending, and take first.
myList.OrderByDescending(m => m.Value.Count()).First().Key;
of course, if you want all the keys with highest count (they may be multiple values with same length), you should do a group by count.
Something like that.
myList.GroupBy(m => m.Value.Count())
.OrderByDescending(m => m.Key)//I'm the key of the group by
.First()
.Select(g => g.Key);//I'm the key of the SortedList
So if you add to your sample an item with same list length
myList.Add(24, new List<string>());
myList[24].AddRange(new[] {"a", "b", "c", "d", "e"});
you will get 23 And 24.
same could be achieved with
from item in myList
let maxCount = myList.Max(x => x.Value.Count())
where item.Value.Count() == maxCount
select item.Key;
Whilst a sort will give you correct results, it requires O(n log n) time to execute, which is asymptotically higher than a simple O(n) sweep:
int maxLength = myList.Max(x => x.Value.Count);
var longestKeys = myList.Where(x => x.Value.Count == maxLength).Select(x => x.Key);
using MaxBy of morelinq
var key = myList.MaxBy(x => x.Value.Count()).Key;
Just for the sake of adding yet another way of doing it, you can achieve this with Linq's Aggregate method like so:
//Extension method
public static long MaxIndex(this SortedList<long, List<string>> list)
{
return list.Aggregate(
new { MaxValue = -1, Key = -1L },
((agg, current) => (current.Value.Count.CompareTo(agg.MaxValue) > 0 || agg.Key == -1) ?
new { MaxValue = current.Value.Count, Key = current.Key } :
new { MaxValue = agg.MaxValue, Key = agg.Key })).
Key;
}
// how the list is defined:
var myList = new SortedList<long, List<string>>();
// EXAMPLE data only:
myList.Add(0, new List<string>());
myList[0].AddRange(new[] { "a", "b", "c" });
myList.Add(8, new List<string>());
myList[8].AddRange(new[] { "1", "2" });
myList.Add(23, new List<string>());
myList[23].AddRange(new[] { "c", "d", "e", "f", "g" });
var idx = myList.MaxIndex();
This is adapted from this SO answer: https://stackoverflow.com/a/15068695/172769
Cheers
I have a function that returns IEnumerable<IEnumerable<string>>. I want to use this function, building on the basic IEnumerable, assigning it to a List<List<string>>. This would be easy if I just had one dimension (i.e. one collection), and would be solved by using the ToList() method - but how do i do this for an IEnumerable<IEnumberable<string>> without iterating through each of the items?
You could use Enumerable.SelectMany
For example(from MSDN)
PetOwner[] petOwners =
{ new PetOwner { Name="Higa, Sidney",
Pets = new List<string>{ "Scruffy", "Sam" } },
new PetOwner { Name="Ashkenazi, Ronen",
Pets = new List<string>{ "Walker", "Sugar" } },
new PetOwner { Name="Price, Vernette",
Pets = new List<string>{ "Scratches", "Diesel" } } };
// Query using SelectMany().
IEnumerable<string> query1 = petOwners.SelectMany(petOwner => petOwner.Pets);
Probably the best resource on this topic(J.Skeet):
Reimplementing LINQ to Objects: Part 9 - SelectMany
Here's another example with a List<List<string>> like in your question:
var lists = new List<List<string>>();
lists.Add(new List<String>(new[] { "A", "B", "C" }));
lists.Add(new List<String>(new[] { "D", "E", "F", "G" }));
lists.Add(new List<String>(new[] { "H", "I", "J", "K", "L" }));
var all = lists.SelectMany(i => i);
String allLetters= String.Format("All letters: {0}",String.Join(",",all));
I 'm assuming you have some kind of IEnumerable<IEnumerable<T>>. You can flatten it using something like this: YourList.SelectMany(x => x).