C# double for comprehension? - c#

Is it possible to do a double for comprehension in C#? For example, the following works:
var a = new[] { 1, 2, 3 };
var b = Enumerable.Range(0, a.Length).Select(i => a[i]).ToArray();
But when I try and adapt this code for the two-dimensional case, things don't work. Below I'm trying to iterate over the pixels of a bitmap:
Color[] p = Enumerable.Range(0, Source.Width).Select(i => Enumerable.Range(0, Source.Height).Select(j => Source.GetPixel(i, j))).ToArray().
Is there any way to get what I want? The error I'm currently getting is:
Cannot implicitly convert type
System.Collections.Generic.IEnumerable[] to
System.Drawing.Color[]

The outer Select needs to be a SelectMany to flatten the projection:
Color[] p = Enumerable.Range(0, Source.Width)
.SelectMany(i => Enumerable.Range(0, Source.Height)
.Select(j => Source.GetPixel(i, j)))
.ToArray();
or to create a jagged 2-D array add an inner ToArray():
Color[][] p = Enumerable.Range(0, Source.Width)
.Select(i => Enumerable.Range(0, Source.Height)
.Select(j => Source.GetPixel(i, j))
.ToArray())
.ToArray();

Related

Group the indexes of the same elements in a array in C#

There is a int[] array that stores different numbers.
What I want is to group the indexes of those same numbers in the array to the same groups.
For exmaple, the array is int[5]{1,2,5,1,5}
I would like to see the output is List<List<int>> { {0,3}, {1}, {2,4} } // don't mind syntax
It's better if Linq (or a more efficient way) can be used, thanks for help.
You can simply use GroupBy and the position obtained from the Select overload:
int[] array;
var result = array.Select((v, idx) => new { Value = v, Index = idx })
.GroupBy(g => g.Value)
.Select(g => g.ToArray()) // inner array
.ToArray(); // outer array
One of ways:
var result = myArray.Select((elem, idx) => new { Value = elem, Idx = idx})
.GroupBy(proxy => proxy.Value);
foreach (var grouped in result)
{
Console.WriteLine("Element {0} has indexes: {1}",
grouped.Key,
string.Join(", ", grouped.Select(proxy => proxy.Idx).ToArray()));
}
var myFinalList = result.Select(proxy => proxy.ToArray()).ToList();
You can use Enumerable.Range combined with GroupBy:
int[] arr = { 1, 2, 5, 1, 5 };
var result = Enumerable.Range(0, arr.Length)
.GroupBy(i => arr[i])
.Select(x => x.ToList()).ToList();
DEMO HERE

Remove all value duplicates in array

example array:
int[] s new = {1,2,3,1};
if use:
int[] inew = snew.Distinct().ToArray();
then out put:
{1,2,3}
but I want out put:
{2,3}
You need to select everything where duplicate count is == 1:
snew.GroupBy(x => x)
.Where(x => x.Count() == 1)
.Select(x => x.First())
.ToArray();
Fiddle here

Preserve order with linq after groupby and selectmany

Is there a way to preserve the order after this linq expression?
var results =
DateList
.GroupBy(x => x.Date.Subtract(firstDay).Days / 7 + 1)
.SelectMany(gx => gx, (gx, x) => new {Week = gx.Key,DateTime =x,Count = gx.Count(),});
I found this Preserving order with LINQ , but I'm not sure if its the GroupBy or SelectMany casing the issues
Yes, if you first select your DateList and combine it with an index, using an overload of .Select that uses a delegate with a second (int) parameter that is called with the index of the items from the sequence :
DateList
.Select((dateTime, idx) => new {dateTime, idx})
.GroupBy(x => x.dateTime.Date.Subtract(firstDay).Days / 7 + 1)
...and persist the value through the linq chain
.SelectMany(gx => gx, (gx, x) => new {Week = gx.Key,
DateTime = x.dateTime,
Count = gx.Count(),
x.idx})
...then use it to re-order the output
.OrderBy(x => x.idx)
...and strip it from your final selection
.Select(x => new {x.Week, x.DateTime, x.Count});
then you can maintain the same order as the original list.
Solution of #spender is good, but can it be done without OrderBy? It can, because we can use the index for direct indexing into array, but it would not be one linq query:
var resultsTmp =
DateList.Select((d, i) => new { d, i })
.GroupBy(x => x.d.Date.Subtract(firstDay).Days / 7 + 1)
.SelectMany(gx => gx, (gx, x) => new { Week = gx.Key, DateTime = x.d, Count = gx.Count(), x.i })
.ToArray();
var resultsTmp2 = resultsTmp.ToArray();
foreach (var r in resultsTmp) { resultsTmp2[r.i] = r; };
var results = resultsTmp2.Select(r => new { r.Week, r.DateTime, r.Count });
It looks a bit complex. I would probably do something more straightforward like:
var DateList2 = DateList.Select(d => new { DateTime = d, Week = d.Subtract(firstDay).Days / 7 + 1 }).ToArray();
var weeks = DateList2.GroupBy(d => d.Week).ToDictionary(k => k.Key, v => v.Count());
var results = DateList2.Select(d2 => new { d2.Week, d2.DateTime, Count = weeks[d2.Week] });

Select lists from lists with linq

I have an list with x items. I wish to get an results that groups this list based of a number and not a property.
For example.
I have a list of 8 items. I want to group them by 3.
I want to get a List thats contains three lists, where the first two lists contains each three items and the last list the remaining two.
I want a more elegant solution than this:
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
var result = new List<List<TrimlinePage>>();
while (!(result.Count != 0 && result.Last().Count % 3 > 0))
{
int skip = result.Count*groupSize;
var group = pages.Skip(skip).Take(groupSize).ToList();
result.Add(group);
}
return result;
}
You can use the integer divison trick:
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index / groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Example:
int groupSize = 3;
var pages = new List<string> { "A", "B", "C", "D", "E", "F", "G" };
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index / groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Result:
foreach(var list in lists)
Console.WriteLine(string.Join(",", list));
Output:
A,B,C
D,E,F
G
So this approach will give you lists with the specified max-size, in this case 3. If you instead want to ensure that you always get three lists you need to use % instead of /:
List<List<string>> lists = pages
.Select((str, index) => new { str, index })
.GroupBy(x => x.index % groupSize)
.Select(g => g.Select(x => x.str).ToList())
.ToList();
Try this:
var list = Enumerable.Range(1,100);
var query = list
.Select((x, i) => new {x, i})
.GroupBy(v => v.i / 3).Select(g => g.Select(v =>v.x.ToList()))
.ToList();
Here's a simple solution using side effects (which is generally discouraged):
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
var i = 0;
return pages.GroupBy(p => i++ / 3, (k, g) => g.ToList()).ToList();
}
Or if you want to avoid relying on side effects, you could use this:
private static List<List<string>> GroupBy(List<string> pages, int groupSize)
{
return pages.Select(p => new { p, i })
.GroupBy(x => x.i / 3)
.Select(g => g.Select(x => x.p).ToList())
.ToList();
}
LINQ is not the best solution. Often good old indexing is much more readable and efficient.
private static List<List<T>> GroupBy(List<T> pages, int groupSize)
{
var result = new List<List<T>>();
List<T> l;
for (int i=0; i < pages.Count; i++)
{
if (i%groupSize == 0)
{
l = new List<T>();
result.Add(l);
}
l.Add(pages[i]);
}
return result;
}
You could also have a look at morelinq which contains the Partition method.
It's available via NuGet.

Splitting byte list into position lists C#

So I have a list of bytes
List<byte> s = {1,2,3,2,2,2,3,1,2,4,2,1,4,.....};
I want to get new position lists using index of element.To something like this...
List<byte> 1 = {0,7,11};
List<byte> 2 = {1,3,4,5,8,10};
List<byte> 3 = {2,6};
List<byte> 4 = {9,12};
List<byte> 5 = ..... and so on
What`s the best way of doing this?
thank you.
You can use GroupBy and ToDictionary to get Dictionary<byte, List<int>>:
var dict = s.Select((value, index) => new { value, index })
.GroupBy(x => x.value)
.ToDictionary(g => g.Key, g => g.Select(x => x.index).ToList());
With LINQ, you can create an ILookup<TKey, TElement> with the desired results like this:
var indicesByByte = s.Select((item, index) => new { Item = item, Index = index } )
.ToLookup(tuple => tuple.Item, tuple => tuple.Index);
Now,
indicesByByte[0] will be a sequence containing {0,7,11}
indicesByByte[1] will be a sequence containing {1,3,4,5,8,10}
etc.
One way to do this is with LINQ, using the overload of Enumerable<T>.Select which contains the index, then grouping:
var groups = s.Select((item, index) => new {index, item})
.GroupBy(x => x.item, x => x.index)
.ToDictionary(x => x.Key, x => x.ToList());
This will return a Dictionary<byte, List<int>> where the key is the value (1, 2, 3, 4, 5 in your example) and the value contains a list of positions.
You could also do it using a for loop, in a single pass:
var groups = new Dictionary<byte, List<int>>();
for (int i = 0; i < s.Count; i++)
{
if(!groups.ContainsKey(s[i]))
groups[s[i]] = new List<int>();
groups[s[i]].Add(i);
}

Categories

Resources