Array operations with n-dimensional array using LINQ (C#) - c#

Assume we have a jagged array
int[][] a = { new[] { 1, 2, 3, 4 }, new[] { 5, 6, 7, 8 }, new[] { 9, 10, 11, 12 } };
To get a sum of second row and sum of second column, it can be written both code lines respectively:
int rowSum = a[1].Sum();
int colSum = a.Select(row => row[1]).Sum();
But if we have definition of 2-dimensional array
int[,] a = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
the above-cited code will not work due to compiller errors:
Error 1 Wrong number of indices inside []; expected 2
Error 2 'int[*,*]' does not contain a definition for 'Select' and no extension method 'Select' accepting a first argument of type 'int[*,*]' could be found (are you missing a using directive or an assembly reference?)
So, the question: How to use LINQ methods with n-dimensional arrays, but not jagged ones? And is where a method to convert rectangular array to jagged?
P.S. I tried to find the answer in documentation, but without result.

LINQ to Objects is based on the IEnumerable<T> Interface, i.e. a one-dimensional sequence of values. This means it doesn't mix well with n-dimensional data structures like non-jagged arrays, although it's possible.
You can generate one-dimensional sequence of integers that index into the n-dimensional array:
int rowSum = Enumerable.Range(0, a.GetLength(1)).Sum(i => a[1, i]);
int colSum = Enumerable.Range(0, a.GetLength(0)).Sum(i => a[i, 1]);

About your question "How to use LINQ methods with n-dimensional arrays":
You can't use most LINQ methods with a n dimensional array, because such an array only implements IEnumerable but not IEnumerable<T> and most of the LINQ extension methods are extension methods for IEnumerable<T>.
About the other question: See dtb's answer.

To add to dtb's solution, a more general way of iterating over all items of the array would be:
int[,] b = { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } };
var flattenedArray = Enumerable.Range(0, b.GetLength(0))
.SelectMany(i => Enumerable.Range(0, b.GetLength(1))
.Select(j => new { Row = i, Col = j }));
And now:
var rowSum2 = flattenedArray.Where(t => t.Row == 1).Sum(t => b[t.Row, t.Col]);
var colSum2 = flattenedArray.Where(t => t.Col == 1).Sum(t => b[t.Row, t.Col]);
Of course this is ultra-wasteful as we are creating coordinate tuples even for those items that we will end up filtering out with Where, but if you don't know what the selection criteria will be beforehand this is the way to go (or not -- this seems more like an excercise than something you 'd want to do in practice).
I can also imagine how this might be extended for arrays of any rank (not just 2D) using a recursive lambda and something like Tuple, but that crosses over into masochism territory.

The 2D array doesn't have any built in way of iterating over a row or column. It's not too difficult to create your own such method though. See this class for an implementation which gets an enumerable for row and column.
public static class LINQTo2DArray
{
public static IEnumerable<T> Row<T>(this T[,] Array, int Row)
{
for (int i = 0; i < Array.GetLength(1); i++)
{
yield return Array[Row, i];
}
}
public static IEnumerable<T> Column<T>(this T[,] Array, int Column)
{
for (int i = 0; i < Array.GetLength(0); i++)
{
yield return Array[i, Column];
}
}
}
You can also flatten the array usinga.Cast<int>() but you would then loose all the info about columns/rows

A simpler way is doing like below
var t = new List<Tuple<int, int>>();
int[][] a = t.Select(x => new int[]{ x.Item1, x.Item2}).ToArray();

The simplest LINQ only approach I can see to do these kinds of row and column operations on a two dimensional array is to define the following lookups:
var cols = a
.OfType<int>()
.Select((x, n) => new { x, n, })
.ToLookup(xn => xn.n % a.GetLength(1), xn => xn.x);
var rows = a
.OfType<int>()
.Select((x, n) => new { x, n, })
.ToLookup(xn => xn.n / a.GetLength(1), xn => xn.x);
Now you can simply do this:
var firstColumnSum = cols[0].Sum();
As for n-dimensional, it just gets too painful... Sorry.

Related

PriorityQueue containing array C#

I would like to create a PriorityQueue to store int[]. The first element in the array is gonna be the criteria for the comparisons.
I could do that easily in Java, though I could not convert it to C#. Could you please guide me?
Priority queues don't work the same way in both languages. What you're trying to do is the Java way of giving PQ a lambda (function) to compare any two elements. In C#, you give each element a priority when adding it to the queue, and then make a comparer to compare different priorities.
PriorityQueue<int[], int> pq = new(Comparer<int>.Create((a, b) => a - b));
// The Comparer compares the *priorities*, not the elements
pq.Enqueue(new int[] { 1, 2, 3, 4 }, 5);
pq.Enqueue(new int[] { 1, 2, 3, 4 }, 0); // This has more priority
while (pq.TryDequeue(out int[]? arr, out int priority))
{
Console.WriteLine(priority); // 0; 5
}
You may be interested in just a simple List and LINQ:
using System.Linq; // at the top of your code to include LINQ
List<int[]> list = new();
list.Add(new int[] { 1, 2, 3, 4 });
list.Add(new int[] { 5, 2, 3, 4 });
IEnumerable<int[]> ordered = list.OrderBy(x => x[0]); // orders by the first element

How to create sublists from a list with linq?

Here is another way Split a List into smaller lists of N size
The purpose of this post is to share knowledge involving "Linq" and opinions without using "for" ties and "ranges" directly.
Example: I have a list of 100 items and I need to make it into 10 lists.
I use the follow script, somebody has a better way or more performatic?
var subLists = myList.Select((x, i) => new { Index = i, Item = x })
.GroupBy(x => x.Index / "MAXIMUM ITEMS ON SUBLIST")
.Select(x => x.Select(v => X.Item).ToList());
It`s a slow operation
(x, i) => new { Index = i, Item = x }
Here's an extension method that will work with any list
public static IEnumerable<List<T>> splitList<T>(List<T> items, int size)
{
for (int i=0; i < items.Count; i+= size)
{
yield return items.GetRange(i, Math.Min(size, items.Count - i));
}
}
OR better performance
public static List<List<T>> splitList<T>(this List<T> items, int size)
{
List<List<T>> list = new List<List<T>>();
for (int i = 0; i < items.Count; i += size)
list.Add(items.GetRange(i, Math.Min(size, items.Count - i)));
return list;
}
Let's create a generic answer. One that works for any sequence of any length, where you want to split the sequence into a sequence of sub-sequences, where every sub-sequence has a specified length, except maybe for the last:
For example:
IEnumerable<int> items = {10, 11, 12, 13, 14, 15, 16, 17};
// split into subsequences of length 3:
IEnumerable<IEnumerable> splitSequence = items.Split(3);
// splitSequence is a sequence of 3 subsequences:
// {10, 11, 12},
// {13, 14, 15},
// {16, 17}
We'll do this by creating an extension method. This way, the method Split can be used as any LINQ function. See extension methods demystified. To make it efficient, I'll enumerate only once, and I don't enumerate any more items than requested for.
IEnumerable<TSource> Split(this IEnumerable<TSource> source, int splitSize)
{
// TODO: exception if null source, or non-positive splitSize
// Get the enumerator and enumerate enough elements to return
IEnumerator<TSource> enumerator = source.GetEnumerator();
while (enumerator.MoveNext())
{
// there are still items in the source; fill a new sub-sequence
var subSequence = new List<Tsource>(SplitSize);
do
{ // add the current item to the list:
subSequence.Add(enumerator.Current);
}
// repeat this until the subSequence is full or until source has no more elements:
while (subSequence.Count() < splitSize && enumerator.MoveNext());
// return the subSequence
yield return subSequence;
}
}
Usage:
// Get all Students that live in New York, split them into groups of 10 Students
// and return groups that have at least one Law Student
var newYorkLasStudentGroups = GetStudents();
.OrderBy(student => student.UniversityLocation == "New York")
.Split(10)
.Where(studentGroup => studentGroup.Any(student => student.Study == "Law"));
This question is not a duplicate. As mentioned, I question a possible form using linq that would be more perfomatic. "For" ties with "range", for example, I am aware of.
Thank you all for your collaboration, comments and possible solutions !!!

Quick sorting a data array while tracking index C#

I'm a bit stuck using quick sort algorithm on an integer array, while saving the original indexes of the elements as they're moved around during the sorting process. Using C#/Visual studio
For example
ToSort Array {52,05,08,66,02,10}
Indexes : 0 1 2 3 4 5
AfterSort Array {02,05,08,10,52,66}
Indexes : 4 1 2 5 0 3
I need to save the indexes of the sorted values in another array.
I feel like this is very complex as quick sorting is recursive and any help or pointers would be much appreciated! Thanks!
As #Will said you can do something like this :
var myArray = new int[] { 52, 05, 08, 66, 02, 10 };
///In tupple item1 you have the number, in the item2 you have the index
var myIndexedArray = myArray.Select( ( n, index ) => Tuple.Create( n, index ) );
///Or if you are using c# 7, you can use the tuple literals ! :
var myIndexedArray = myArray.Select( ( n, index ) => ( n, index ) );
///Call your quick sort method, sort by the item1 (the number) inside the method
// or use Enumerable.OrderBy:
myIndexedArray = myIndexedArray.OrderBy(x => x.Item1);
///Then get your things back
int[] numbers = myIndexedArray.Select(x => x.Item1).ToArray();
int[] indexes = myIndexedArray.Select(x => x.Item2).ToArray();
LINQ OrderBy uses QuickSort internally. So instead of implementing QuickSort yourself, use OrderBy, if needed with a custom IComparer<T>.
Put the data to be sorted into an anonymous type which remembers the original index, then sort by value. You can retrieve the original index from the index property of the sorted elements.
using System.Linq;
var data = new int[] { 52,05,08,66,02,10 };
var sortingDictionary = data
.Select((value, index) => new { value, index });
var sorted = sortingDictionary
.OrderBy(kvp => kvp.value)
.ToList(); // enumerate before looping over result!
for (var newIndex = 0; newIndex < sorted.Count(); newIndex ++) {
var item = sorted.ElementAt(newIndex);
Console.WriteLine(
$"New index: {newIndex}, old index: {item.index}, value: {item.value}"
);
}
Fiddle
Edit: incorporated improvements suggested by mjwills

C# How to make recursive function to return the nth most common integer in an array of integers

C# How to make recursive function to return the nth most common integer in an array of integers
I am using c# and I am looking for the most memory efficient way to sort a list of integers by how often they appear in an integer array and then return the nth array element where nth is an integer represent the descending order key of choice (most used integer, 2nd most used integer, 3rd most used integer, etc.
I can do this using linq with something like this...
public static void Main(string[] args)
{
int x = NthMostCommon(new int[] { 5, 4, 3, 2, 1, 5, 4, 3, 2, 5, 4, 3, 5, 4, 5 }, 2);
Console.WriteLine(x);
}
private static int NthMostCommon(int[] a, int k)
{
int result = 0;
var query = a.GroupBy(item => item).OrderByDescending(g => g.Count());
if (query.Count() >= k)
{
result = query.ElementAt(k - 1).Key;
}
return result;
}
This works, but I have been told that this is not the most memory efficient way of getting the desired result when working with larger integer arrays. I cannot see how I can reduce the memory footprint. I have to iterate the entire array, regardless of the size. Yes?
Any ideas?
Thanks in advance.
This article may help you.
http://www.developerfusion.com/article/84468/linq-to-log-files/
the most common integer could be more than 1 integer (see the int array in my code), so I make the function to return int[] instead of just int.
I also have GroupBy which in worst case (identical integer in the input array) could be the same efficient as the previous one. You may also be able to rewrite it.
public static void Main(string[] args)
{
int[] x = NthMostCommon(new int[] { 1, 2, 2, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 6 }, 2);
Console.WriteLine(x);
}
private static int[] NthMostCommon(int[] a, int k)
{
var query = GroupAndCount(a)
.GroupBy(x => x.Value)
.ToDictionary(x => x.Key, x => x.Select(n => n.Key))
.OrderByDescending(x => x.Key);
if (query.Count() >= k)
{
return query.ElementAt(k-1).Value.ToArray();
}
return null;
}
public static IEnumerable<KeyValuePair<T, int>> GroupAndCount<T>
(IEnumerable<T> source)
{
Dictionary<T, int> dictionary =
new Dictionary<T, int>();
foreach (T element in source)
{
if (dictionary.ContainsKey(element))
{
dictionary[element]++;
}
else {
dictionary[element] = 1;
}
}
return dictionary;
}
Where k << n and 0 < x < k, where x is an integer, start with a Counting Sort and modify it.
A counting sort takes O(n + k) memory and O(n + k) time.
In computer science, counting sort is an algorithm for sorting a collection of objects according to keys that are small integers; that is, it is an integer sorting algorithm. It operates by counting the number of objects that have each distinct key value, and using arithmetic on those counts to determine the positions of each key value in the output sequence.
Its running time is linear in the number of items and the difference between the maximum and minimum key values, so it is only suitable for direct use in situations where the variation in keys is not significantly greater than the number of items. However, it is often used as a subroutine in another sorting algorithm, radix sort, that can handle larger keys more efficiently.
After the items are sorted according to frequency it's simply a matter of walking the result.
If there is really a "requirement" to be recursive the procedural loop code can be converted into recursive calls, at the expense of stackframes. Using LINQ - which provides various higher-order functions - often trivial eliminates the need for recursion.

How do I sum a list<> of arrays

I have a List< int[] > myList, where I know that all the int[] arrays are the same length - for the sake of argument, let us say I have 500 arrays, each is 2048 elements long. I'd like to sum all 500 of these arrays, to give me a single array, 2048 elements long, where each element is the sum of all the same positions in all the other arrays.
Obviously this is trivial in imperative code:
int[] sums = new int[myList[0].Length];
foreach(int[] array in myList)
{
for(int i = 0; i < sums.Length; i++)
{
sums[i] += array[i];
}
}
But I was wondering if there was a nice Linq or Enumerable.xxx technique?
Edit: Ouch...This became a bit harder while I wasn't looking. Changing requirements can be a real PITA.
Okay, so take each position in the array, and sum it:
var sums = Enumerable.Range(0, myList[0].Length)
.Select(i => myList.Select(
nums => nums[i]
).Sum()
);
That's kind of ugly...but I think the statement version would be even worse.
EDIT: I've left this here for the sake of interest, but the accepted answer is much nicer.
EDIT: Okay, my previous attempt (see edit history) was basically completely wrong...
You can do this with a single line of LINQ, but it's horrible:
var results = myList.SelectMany(array => array.Select(
(value, index) => new { value, index })
.Aggregate(new int[myList[0].Length],
(result, item) => { result[item.index] += value; return result; });
I haven't tested it, but I think it should work. I wouldn't recommend it though. The SelectMany flattens all the data into a sequence of pairs - each pair is the value, and its index within its original array.
The Aggregate step is entirely non-pure - it modifies its accumulator as it goes, by adding the right value at the right point.
Unless anyone can think of a way of basically pivoting your original data (at which point my earlier answer is what you want) I suspect you're best off doing this the non-LINQ way.
This works with any 2 sequences, not just arrays:
var myList = new List<int[]>
{
new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
new int[] { 10, 20, 30, 40, 50, 60, 70, 80, 90 }
};
var sums =
from array in myList
from valueIndex in array.Select((value, index) => new { Value = value, Index = index })
group valueIndex by valueIndex.Index into indexGroups
select indexGroups.Select(indexGroup => indexGroup.Value).Sum()
foreach(var sum in sums)
{
Console.WriteLine(sum);
}
// Prints:
//
// 11
// 22
// 33
// 44
// 55
// 66
// 77
// 88
// 99
OK, assuming we can assume that the sum of the ints at each position over the list of arrays will itself fit into an int (which is a dodgy assumption, but I'll make it anyway to make the job easier):
int[] sums =
Enumerable.Range(0, listOfArrays[0].Length-1).
Select(sumTotal =>
Enumerable.Range(0, listOfArrays.Count-1).
Aggregate((total, listIndex) =>
total += listOfArrays[listIndex][sumTotal])).ToArray();
EDIT - D'oh. For some reason .Select evaded me originally. That's a bit better. It's a slight hack because sumTotal is acting as both the input (the position in the array which is used in the Aggregate call) and the output sum in the resulting IEnumerable, which is counter-intuitive.
Frankly this is far more horrible than doing it the old-fasioned way :-)
Here is one that trades the Linq statement simplicity with performance.
var colSums =
from col in array.Pivot()
select col.Sum();
public static class LinqExtensions {
public static IEnumerable<IEnumerable<T>> Pivot<T>( this IList<T[]> array ) {
for( int c = 0; c < array[ 0 ].Length; c++ )
yield return PivotColumn( array, c );
}
private static IEnumerable<T> PivotColumn<T>( IList<T[]> array, int c ) {
for( int r = 0; r < array.Count; r++ )
yield return array[ r ][ c ];
}
}
I would do it as follows … but this solution might actually be very slow so you might want to run a benchmark before deploying it in performance-critical sections.
var result = xs.Aggregate(
(a, b) => Enumerable.Range(0, a.Length).Select(i => a[i] + b[i]).ToArray()
);
It can be done with Zip and Aggregate. The question is so old that probably Zip was not around at the time. Anyway, here is my version, hoping it will help someone.
List<int[]> myListOfIntArrays = PopulateListOfArraysOf100Ints();
int[] totals = new int[100];
int[] allArraysSum = myListOfIntArrays.Aggregate(
totals,
(arrCumul, arrItem) => arrCumul.Zip(arrItem, (a, b) => a + b))
.ToArray();

Categories

Resources