How do you transpose dimensions in a 2D collection using LINQ? - c#

Consider the following structure:
IEnumerable<IEnumerable<int>> collection = new[] {
new [] {1, 2, 3},
new [] {4, 5, 6},
new [] {7, 8, 9}
};
How can I enumerate this collection so that I obtain IEnumerable<int> collections made up of the first items, second items, etc.?
That is, {1, 4, 7}, {2, 5, 8}, ...
(Though the implementation I've chosen is int[] objects, assume you only have IEnumerable<int> functionality. Thanks.)

Here's an approach that uses a generator instead of recursion. There's less array construction too, so it might be faster, but that's totally conjecture.
public static IEnumerable<IEnumerable<T>> Transpose<T>(
this IEnumerable<IEnumerable<T>> #this)
{
var enumerators = #this.Select(t => t.GetEnumerator())
.Where(e => e.MoveNext());
while (enumerators.Any()) {
yield return enumerators.Select(e => e.Current);
enumerators = enumerators.Where(e => e.MoveNext());
}
}

Just my 2 cents
In pure linq:
var transpond = collection.First().Select((frow,i)=>collection.Select(row=>row.ElementAt(i)));
Or with some inpurity:
var r1 = collection.First().Select((frow, i) => collection.Select(row => row.ToArray()[i]));

Code credit goes here (untested but looks fine).
public static class LinqExtensions
{
public static IEnumerable<IEnumerable<T>> Transpose<T>(this IEnumerable<IEnumerable<T>> values)
{
if (!values.Any())
return values;
if (!values.First().Any())
return Transpose(values.Skip(1));
var x = values.First().First();
var xs = values.First().Skip(1);
var xss = values.Skip(1);
return
new[] {new[] {x}
.Concat(xss.Select(ht => ht.First()))}
.Concat(new[] { xs }
.Concat(xss.Select(ht => ht.Skip(1)))
.Transpose());
}
}
//Input: transpose [[1,2,3],[4,5,6],[7,8,9]]
//Output: [[1,4,7],[2,5,8],[3,6,9]]
var result = new[] {new[] {1, 2, 3}, new[] {4, 5, 6}, new[] {7, 8, 9}}.Transpose();

Assuming all the sequences are of the same length.
static void Main(string[] args)
{
IEnumerable<IEnumerable<int>> collection =
new[]
{
new [] {1, 2, 3},
new [] {4, 5, 6 },
new [] {7, 8, 9}
};
Console.WriteLine("\tInitial");
Print(collection);
var transposed =
Enumerable.Range(0, collection.First().Count())
.Select(i => collection.Select(j => j.ElementAt(i)));
Console.WriteLine("\tTransposed");
Print(transposed);
}
static void Print<T>(IEnumerable<IEnumerable<T>> collection)=>
Console.WriteLine(string.Join(Environment.NewLine, collection.Select(i => string.Join(" ", i))));
Gives:
Initial
1 2 3
4 5 6
7 8 9
Transposed
1 4 7
2 5 8
3 6 9

If all elements are guaranteed to be the same length, you could do this:
IEnumerable<IEnumerable<int>> Transpose(IEnumerable<IEnumerable<int>> collection)
{
var width = collection.First().Count();
var flattened = collection.SelectMany(c => c).ToArray();
var height = flattened.Length / width;
var result = new int[width][];
for (int i = 0; i < width; i++)
{
result[i] = new int[height];
for (int j = i, k = 0; j < flattened.Length; j += width, k++)
result[i][k] = flattened[j];
}
return result;
}

Related

How to Zip two Lists of different size to create a new list that is same as the size of the longest amongst the original lists?

I have two C# Lists of different sizes e.g.
List<int> list1 = new List<int>{1,2,3,4,5,6,7};
List<int> list2 = new List<int>{4,5,6,7,8,9};
I want to use the linq Zip method to combine these two into a list of tuples that is of the size list1. Here is the resulting list I am looking for
{(1,4), (2,5), (3,6), (4,7), (5,8), (6,9), (7,0)} //this is of type List<(int,int)
Since the last item of list1 does not has a counterpart in list2, I fill up my last item of the resulting list with a default value (in this case 0 as in my case it will never appear in any of the original lists).
Is there a way I can use the linq Zip method alone to achieve this?
You can use Concat to make them both the same size, and then zip it:
var zipped = list1.Concat(Enumerable.Repeat(0,Math.Max(list2.Count-list1.Count,0)))
.Zip(list2.Concat(Enumerable.Repeat(0,Math.Max(list1.Count-list2.Count,0))),
(a,b)=>(a,b));
Or create an extension method:
public static class ZipExtension{
public static IEnumerable<TResult> Zip<TFirst,TSecond,TResult>(
this IEnumerable<TFirst> first,
IEnumerable<TSecond> second,
Func<TFirst,TSecond,TResult> func,
TFirst padder1,
TSecond padder2)
{
var firstExp = first.Concat(
Enumerable.Repeat(
padder1,
Math.Max(second.Count()-first.Count(),0)
)
);
var secExp = second.Concat(
Enumerable.Repeat(
padder2,
Math.Max(first.Count()-second.Count(),0)
)
);
return firstExp.Zip(secExp, (a,b) => func(a,b));
}
}
So you can use like this:
//last 2 arguments are the padder values for list1 and list2
var zipped = list1.Zip(list2, (a,b) => (a,b), 0, 0);
There is a useful and popular MoreLinq library. Install it and use.
using MoreLinq;
var result = list1.ZipLongest(list2, (x, y) => (x, y));
Try this using Zip function-
static void Main(string[] args)
{
List<int> firstList = new List<int>() { 1, 2, 3, 4, 5, 6, 0, 34, 56, 23 };
List<int> secondList = new List<int>() { 4, 5, 6, 7, 8, 9, 1 };
int a = firstList.Count;
int b = secondList.Count;
for (int k = 0; k < (a - b); k++)
{
if(a>b)
secondList.Add(0);
else
firstList.Add(0);
}
var zipArray = firstList.Zip(secondList, (c, d) => c + " " + d);
foreach(var item in zipArray)
{
Console.WriteLine(item);
}
Console.Read();
}
Or you can try this using ZipLongest Function by installing MoreLinq nuget package-
static void Main(string[] args)
{
List<int> firstList = new List<int>() { 1, 2, 3, 4, 5, 6, 0, 34, 56, 23 };
List<int> secondList = new List<int>() { 4, 5, 6, 7, 8, 9, 1 };
var zipArray = firstList.ZipLongest(secondList, (c, d) => (c,d));
foreach (var item in zipArray)
{
Console.WriteLine(item);
}
Console.Read();
}
Try this code-
static void Main(string[] args)
{
List<int> firstList=new List<int>() { 1, 2, 3, 4, 5, 6,0,34,56,23};
List<int> secondList=new List<int>() { 4, 5, 6, 7, 8, 9,1};
int a = firstList.Count;
int b = secondList.Count;
if (a > b)
{
for(int k=0;k<(a-b);k++)
secondList.Add(0);
}
else
{
for (int k = 0; k < (b-a); k++)
firstList.Add(0);
}
for(int i=0;i<firstList.Count;i++)
{
for(int j=0;j<=secondList.Count;j++)
{
if(i==j)
Console.Write($"({Convert.ToInt32(firstList[i])},{ Convert.ToInt32(secondList[j])})" + "");
}
}
Console.Read();
}

Add index position Value of an array of array using Linq

We can do sum using arr.Sum() function. But if it is an array of arrays. How will we add all values.
suppose data is
Array/List is [[1,2,3],[3,4,5],[5,4,3]]
how will you get s1 , sum of all first index value, s2 , sum of second index value and so on using LINQ.
If you want to sum up columns' values with a help of Linq:
int[][] source = new int[][] {
new int[] { 1, 2, 3},
new int[] { 3, 4, 5},
new int[] { 5, 4, 3},
};
int maxCol = source.Max(item => item.Length);
var colsSum = Enumerable
.Range(0, maxCol)
.Select(index => source.Sum(item => item.Length > index ? item[index] : 0))
.ToArray(); // let's meaterialize into an array
Test:
Console.Write(string.Join(", ", colsSum));
Outcome:
9, 10, 11
Summing up lines' values is easier:
// [6, 12, 12]
var linesSum = source
.Select(item => item.Sum())
.ToArray();
If you want total sum:
// 30
var total = source
.Select(item => item.Sum())
.Sum();
or
// 30
var total = source
.SelectMany(item => item)
.Sum();
Use combination of Aggregate and Zip
var arrays = new[]
{
new[] { 1, 2, 3 },
new[] { 3, 4, 5 },
new[] { 5, 4, 3 }
};
var result =
arrays.Aggregate(Enumerable.Repeat(0, 3),
(total, array) => total.Zip(array, (sum, current) => sum + current));
// result = { 9, 10, 11 }
Enumerable<T>.Zip executes provided function with items of same index.
A possible LINQ based approach (which will handle variable number of columns in each row):
using System;
using System.Collections.Generic;
using System.Linq;
namespace Test
{
public class Program
{
private static IEnumerable<int> GetTotalsPerColumn(int[][] inputData)
{
var data = inputData.SelectMany(z =>
{
return z.Select((item, index) => new { item, index });
})
.GroupBy(z => z.index)
.OrderBy(z => z.Key)
.Select(y => y.Select(z => z.item).Sum()
);
return data;
}
static void Main(string[] args)
{
var inputData = new[] {
new[] { 1, 2, 3, 5},
new[] { 3, 4, 5, 6},
new[] { 5, 4, 3},
};
var values = GetTotalsPerColumn(inputData);
foreach (var value in values)
{
Console.WriteLine(value);
}
Console.ReadLine();
}
}
}
If you are happy to avoid LINQ, this is another approach you could consider.
GetTotalsPerColumn populates a Dictionary where the key is the column number, and the value is the sum.
using System;
using System.Collections.Generic;
namespace Test
{
public class Program
{
static void Main(string[] args)
{
var inputData = new[] {
new[] { 1, 2, 3, 5},
new[] { 3, 4, 5, 6},
new[] { 5, 4, 3},
};
var values = GetTotalsPerColumn(inputData);
foreach (var value in values)
{
Console.WriteLine(value.Key + " - " + value.Value);
}
Console.ReadLine();
}
private static Dictionary<int, int> GetTotalsPerColumn(int[][] inputData)
{
var values = new Dictionary<int, int>();
foreach (var line in inputData)
{
for (int i = 0; i < line.Length; i++)
{
int tempValue;
values.TryGetValue(i, out tempValue);
tempValue += line[i];
values[i] = tempValue;
}
}
return values;
}
}
}

How to sum up every array of a List<int[]> or jagged array via index?

I want to convert this part of code to LINQ.
Can anyone help me?
var list = new List<int[]>();
list.Add(new int[] { 1, 2, 3, 4 });
list.Add(new int[] { 5, 4, 2, 1 });
list.Add(new int[] { 5, 9, 3, 5 });
var result = new int[4];
foreach (var item in list)
{
for (int i = 0; i < 4; i++)
{
result[i] += item[i];
}
}
Result must be : { 11, 15, 8, 10 } because that is the sum-result
I think this is the most readable version. No need to GroupBy, you can Sum every index of every array:
int[] result = Enumerable.Range(0, 4)
.Select(index => list.Sum(arr => arr[index]))
.ToArray();
Since OP is also using a for-loop from 0-3 they all seem to have the same size.
If that's not the case you could use this super safe approach:
int maxLength = list.Max(arr => arr.Length);
int[] result = Enumerable.Range(0, maxLength)
.Select(index => list.Sum(arr => arr.ElementAtOrDefault(index)))
.ToArray();
First thing that pops to my head:
var list = new List<int[]>();
list.Add(new int[] { 1, 2, 3, 4 });
list.Add(new int[] { 5, 4, 2, 1 });
list.Add(new int[] { 5, 9, 3, 5 });
var result = list.SelectMany(item => item.Select((innerItem, index) => new { index, innerItem }))
.GroupBy(item => item.index, (key, group) => group.Sum(item => item.innerItem))
.ToList();
Tim's approach above is cleaner and is better
You can try this one
var list = new List<int[]>();
list.Add(new int[] { 1, 2, 3, 4 });
list.Add(new int[] { 5, 4, 2, 1 });
list.Add(new int[] { 5, 9, 3, 5 });
var result = list.SelectMany(x => x.Select((z, i) => new {z, i}))
.GroupBy(x=>x.i).Select(x=>x.Sum(z=>z.z)).ToArray();
Want to do aggregation, so why would not use linq aggregate?
var list = new List<int[]>();
list.Add(new int[] { 1, 2, 3, 4 });
list.Add(new int[] { 5, 4, 2, 1 });
list.Add(new int[] { 5, 9, 3, 5 });
var addArrayValues = new Func<int[], int[], int[]>(
(source, destination) =>
{
for (int i = 0; i < source.Length; i++)
destination[i] += source[i];
return destination;
});
var aggregateResult = list.Aggregate(new int[4],
(accumulator, current) => addArrayValues(current, accumulator));

How to compare arrays in a list [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I have a List contains arrays in various sizes. For example:
0 and 7. arrrays have same data {1,2,3,4,5,6}
2, 4 and 5. arrays have same data {1,2,3}
Attention! 3 and 6 doesn't have same data [3] = {1,2} , [6] = {1,3}
I want to get which indexes have same data and add this indexes to another List. For example
anotherList[ 0 ] = {0,7}
anotherList[ 1 ] = {2,4,5}
How can I do this?
Thanks in advance.
You can use this code (Linq i.e. SequenceEqual is very helpful here):
private static IList<IList<int>> EqualArrays(List<int[]> list) {
IList<IList<int>> result = new List<IList<int>>();
HashSet<int> proceeded = new HashSet<int>();
for (int i = 0; i < list.Count; ++i) {
if (proceeded.Contains(i))
continue;
int[] item = list[i];
List<int> equals = new List<int>() { i };
result.Add(equals);
for (int j = i + 1; j < list.Count; ++j)
if (item.SequenceEqual(list[j])) {
equals.Add(j);
proceeded.Add(j);
}
}
return result;
}
...
// Your test case:
List<int[]> list = new List<int[]>() {
new int[] {1, 2, 3, 4, 5, 5, 7},
new int[] {1},
new int[] {1, 2, 3},
new int[] {1, 2},
new int[] {1, 2, 3},
new int[] {1, 2, 3},
new int[] {1, 3},
new int[] {1, 2, 3, 4, 5, 5, 7}
};
// anotherList == {{0, 7}, {1}, {2, 4, 5}, {3}, {6}}
IList<IList<int>> anotherList = EqualArrays(list);
Try this
List<byte[]> anotherList = new List<byte[]>();
foreach (byte[] array in list2)
if (!list1.Any(a => a.SequenceEqual(array)))
anotherList.Add(array);
int[][] original =
{
new [] {1,2,3,4,5,6},
new [] {1},
new [] {1,2,3},
new [] {1,2},
new [] {1,2,3},
new [] {1,2,3},
new [] {1,3},
new [] {1,2,3,4,5,6},
};
int[][] anotherList =
original.Select((values, index) => new { values, index })
.GroupBy(x => x.values, SequenceComparer<int>.Default)
.Where(grouping => grouping.Count() > 1) // optional
.Select(grouping => grouping.Select(x => x.index).ToArray())
.ToArray();
I've adapted the definition for SequenceComparer<T> from here (and here):
public class SequenceComparer<T> : IEqualityComparer<IEnumerable<T>>
{
public static readonly SequenceComparer<T> Default = new SequenceComparer<T>();
public bool Equals(IEnumerable<T> x, IEnumerable<T> y)
{
if (Object.ReferenceEquals(x, y))
return true;
return x != null && y != null && x.SequenceEqual(y);
}
public int GetHashCode(IEnumerable<T> seq)
{
if (seq == null)
return 0;
unchecked
{
const int p = 16777619;
const int hash = (int)2166136261;
return seq.Select(e => e.GetHashCode())
.Aggregate(hash, (a, b) => (a ^ b) * p));
}
}
}

LINQ: Separating single list to multiple lists

I have a single array with these entries:
{1, 1, 2, 2, 3,3,3, 4}
and i want to transform them to ( 3 lists in this case ):
{1,2,3,4}
{1,2,3}
{3}
Is there any way to do this with LINQ or SQL? I guess there's a mathematical term for this operation, which I don't know unfortunately...
Or do I have to do it with loops?
=======
EDIT: I can't really describe the logic, so here are more examples.. It more or less loops multiple times over the array and takes every number once ( but every number only once per round ) until there are no numbers left
{1, 1, 2, 2, 3,3,3, 4, 5}
would be
{1,2,3,4,5}
{1,2,3}
{3}
or
{1, 1, 2, 2,2, 3,3,3, 4, 5}
would be
{1,2,3,4,5}
{1,2,3}
{2,3}
private IEnumerable<List<int>> FooSplit(IEnumerable<int> items)
{
List<int> source = new List<int>(items);
while (source.Any())
{
var result = source.Distinct().ToList();
yield return result;
result.ForEach(item => source.Remove(item));
}
}
Usage:
int[] items = { 1, 1, 2, 2, 3, 3, 3, 4 };
foreach(var subList in FooSplit(items))
{
// here you have your three sublists
}
Here is another solution, which is less readable but it will have better performance:
private IEnumerable<IEnumerable<int>> FooSplit(IEnumerable<int> items)
{
var groups = items.GroupBy(i => i).Select(g => g.ToList()).ToList();
while (groups.Count > 0)
{
yield return groups.Select( g =>
{ var i = g[0]; g.RemoveAt(g.Count - 1); return i; });
groups.RemoveAll(g => g.Count == 0);
}
}
this does the job:
static void Main(string[] args)
{
int[] numbers = {1, 1, 2, 2, 3, 3, 3, 3, 4, 5, 5};
List<int> nums = new List<int>(numbers.Length);
nums.AddRange(numbers);
while (nums.Count > 0)
{
int[] n = nums.Distinct().ToArray();
for (int i = 0; i < n.Count(); i++)
{
Console.Write("{0}\t", n[i]);
nums.Remove(n[i]);
}
Console.WriteLine();
}
Console.Read();
}
Here's an alternative console app:
class Program
{
class Freq
{
public int Num { get; set; }
public int Count { get; set; }
}
static void Main(string[] args)
{
var nums = new[] { 1, 1, 2, 2, 3, 3, 3, 4 };
var groups = nums.GroupBy(i => i).Select(g => new Freq { Num = g.Key, Count = g.Count() }).ToList();
while (groups.Any(g => g.Count > 0))
{
var list = groups.Where(g => g.Count > 0).Select(g => g.Num).ToList();
list.ForEach(li => groups.First(g => g.Num == li).Count--);
Console.WriteLine(String.Join(",", list));
}
Console.ReadKey();
}
}

Categories