How to convert a multi-dimensional array to a dictionary? - c#

I have a n-by-3 array I wish to convert to a Dictionary<string,string[]> where the first column is the key and the rest of the column as an array for the value.
For example:
Key = arr[0,0], Value = new string[2] {arr[0,1], arr[0,2]}.
I'm aware of ToDictionary but I don't know how to set the value part.
arr.ToDictionary(x=>arr[x,0],x=>new string[2]{arr[x,1],arr[x,2]});
//This doesn't work!!!
How can I set it up correctly?

Multidimensional arrays are a continuous block of memory, so you kind of have to treat them like a single array. Try this:
var dict = arr.Cast<string>()
.Select((s, i) => new { s, i })
.GroupBy(s => s.i / arr.GetLength(1))
.ToDictionary(
g => g.First().s,
g => g.Skip(1).Select(i => i.s).ToArray()
);
With explanations:
// First, cast it to an IEnumerable<string>
var dict = arr.Cast<string>()
// Use the Select overload that lets us get the index of the element,
// And we capture the element's index (i), along with the element itself (s)
// and put them together into an anonymous type [1]
.Select((s, i) => new { s, i })
// .GetLength(dimension) is a method on multidimensional arrays to
// get the length of a given dimension (pretty self-explanatory)
// In this case, we want the second dimension, or how wide each
// row is: [x,y] <- we want y
// Divide the element index (s.i) by that length to get the row index
// for that element
.GroupBy(s => s.i / arr.GetLength(1))
// Now we have an Grouping<int, IEnumerable<anonymous{string,int}>>
.ToDictionary(
// We don't care about the key, since it's the row index, what we want
// is the string value (the `s` property) from first element in the row
g => g.First().s,
// For the value, we want to skip the first element, and extract
// the string values (the `s` property), and then convert to an array
g => g.Skip(1).Select(i => i.s).ToArray()
);
[1]: See here for documentation on anonymous types.

Sometimes not using linq is easier to read and faster:
var dict = new Dictionary<string, string[]>();
for (int i = 0; i < arr.GetLength(0); i++)
dict[arr[i, 0]] = new string[] { arr[i, 1], arr[i, 2] };
But when you feel like you REALLY need to use linq:
Enumerable.Range(0, arr.GetLength(0))
.ToDictionary(i => arr[i, 0], i => new string[] {arr[i, 1], arr[i, 2]});

This is the simplest approach I can come up with:
var arr = new int[4, 3]
{
{ 1, 2, 3 },
{ 3, 5, 7 },
{ 5, 8, 11 },
{ 7, 11, 15 },
};
var dict = arr.Cast<int>().Buffer(3).ToDictionary(x => x[0], x => x.Skip(1).ToArray());
That gives me:
You just need to NuGet "System.Interactive" to get the Buffer operator.
Or use this implementation:
public static IEnumerable<T[]> Buffer<T>(this IEnumerable<T> source, int count)
=>
source
.Select((t, i) => new { t, i })
.GroupBy(x => x.i / count)
.Select(x => x.Select(y => y.t).ToArray());

I guess your approach was right ,my only doubt is that your array is static or not?
arr.Select((value, index) => new { value, index }) .ToDictionary(x => x.index, x => new string[2]{x.value[x.index][1],x.value[x.index][2]}));
Note: I couldn't execute and check the code ! Sorry.

I had done using Integer. Please Change for your requirements.
public static void Main()
{
int row=0 , col=0;
int[,] array = new int[,]
{
{ 1, 2, 3 },
{ 4, 5, 6 },
{ 7, 8, 9 },
{ 10, 11, 12 }
};
int flag=0;
for (int i = 0; i < array.Rank; i++)
{
if(flag==0)
{
row= array.GetLength(i);
flag=1;
}
else
{
col= array.GetLength(i);
}
}
Dictionary<int,int[,]> dictionary = new Dictionary<int, int[,]>();
for(int i=0;i<row;i++)
{
dictionary.Add(array[i,0],new int[, ]{{array[i,1]},{array[i,2]}});
}
Console.WriteLine(dictionary[4].GetValue(0,0));
}

Related

Converting a 2d array into a 2d array of a different type. int[,] => ushort[,]

Im trying to find out a way to convert a 2d array of one type to another in a single line of code.
This is a personal learning experience rather than a need to do it in one line!!
Ive gotten so far as to convert it into IEnumerable<Tuple<ushort,ushort>>. Not sure where to go from here.
int[,] X = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var Result = (from e in X.OfType<int>() select e)
.Select(S => (ushort)S)
.Select((value, index) => new { Index = index, Value = value })
.GroupBy(x => x.Index / 2)
.Select(g => new ushort[,] { { g.ElementAt(0).Value,
g.ElementAt(1).Value } });
Need to somehow convert the collection of Tuples into a ushort[,]
EDIT:
Just clarifying the question.
How do I convert a int 2d array into a ushort 2d array using a single line of code in linq?
EDIT:
Ive updated my code.
I now have it resulting in a IEnumerable collection of ushort[,].
I need to now find a way to concatonate all these into a single ushort[,]
The best I could come up with to keep the result two dimensional is this:
var input = new [,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
var output = new ushort[input.GetUpperBound(0) + 1, input.GetUpperBound(1) + 1];
Buffer.BlockCopy(input.Cast<int>().Select(x => (ushort)x).ToArray(), 0, output, 0, input.GetLength(0) * input.GetLength(1) * sizeof(ushort));
Using explicit cast to ushort we could do this, I'm leaving it to you to explore consequences in the conversion and addressing them.
int[,] X = new int[,] { { 1, 2 }, { 3, 4 }, { 5, 6 } };
ushort[,] shortArray = new ushort[X.GetUpperBound(0)+1, X.GetUpperBound(1)+1];
for (int i = 0; i <= X.GetUpperBound(0); ++i)
{
for(int j=0;j<= X.GetUpperBound(1);j++)
shortArray[i, j] = (ushort)X[i,j];
}
In case you are interested on Jagged array instead of multidimensional array, use this.
var jagged = X.Cast<int>()
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / (X.GetUpperBound(1) +1))
.Select(x => x.Select(s=> (ushort)s.Value).ToArray())
.ToArray();
Working example
How about:
var Result = X.OfType<int>().Select(s => new { Index = (s + 1) / 2, Value = s})
.GroupBy(g => g.Index)
.Select(s => s.Select(g => (ushort)g.Value).ToArray())
.ToArray();

finding all possible sum of two arrays element

I have two arrays and i am trying to get all possible sum of each element with other element of two array and index of each element
int[] width = new int[2] {10,20 };
int[] height = new int[2] {30,40 };
result should like this (value / indexes)
10 width0
10+20 width0+width1
10+30 width0+height0
10+40 width0+height1
10+20+30 width0+width1+height0
10+20+40 width0+width1+height1
10+20+30+40 width0+width1+height0+height1
And so for each element in two array
I tried using permutation but I get other output
It is more easy to get all combinations from one array than two arrays. And as we see, you need to store indices and array names along with the value of the elements in collections. So, in my opinion the best option is to combine these two arrays in one dictionary, where the key will be the value of the numbers and the value will be [ArrayName + Index of item] (f.e width0, height1 and so on....)
So, let's combine these arrays in one dictionary:
int[] width = new int[2] { 10, 20 };
int[] height = new int[2] { 30, 40 };
var widthDictionary = width.ToList().Select((number, index) => new { index, number })
.ToDictionary(key => key.number, value => string.Format("width{0}", value.index));
var heightDictionary = height.ToList().Select((number, index) => new { index, number })
.ToDictionary(key => key.number, value => string.Format("height{0}", value.index));
// And here is the final dictionary
var totalDictionary = widthDictionary.Union(heightDictionary);
Then add this method to your class: (source)
public static IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}
Then send your dictionary as an argument to this method and project this collection as you want with the help of the Select() method:
var sumOfCombinations = GetPowerSet(totalDictionary.ToList())
.Where(x => x.Count() > 0)
.Select(x => new
{
Numbers = x.Select(pair => pair.Key).ToList(),
DisplayValues = x.Select(pair => pair.Value).ToList()
})
.ToList();
And at the end you can display expected result as this:
sumOfCombinations.ForEach(x =>
{
x.Numbers.ForEach(number => Console.Write("{0} ", number));
x.DisplayValues.ForEach(displayValue => Console.Write("{0} ", displayValue));
Console.WriteLine();
});
And, the result is:
This is a play off of #Farhad Jabiyev's answer.
Declares a class called IndexValuePair. and uses foreach on widthList and heightList. to populate the 'Index' property of item instance.
Note: Index is a string.
Class & Static Function
public class IndexValuePair
{
public string Index {get;set;}
public int Value {get;set;}
}
public static IEnumerable<IEnumerable<T>> GetPowerSet<T>(List<T> list)
{
return from m in Enumerable.Range(0, 1 << list.Count)
select
from i in Enumerable.Range(0, list.Count)
where (m & (1 << i)) != 0
select list[i];
}
Main (Console)
static void Main(string[] args)
{
int[] width = new int[2] { 10, 20 };
int[] height = new int[2] { 30, 40 };
var wholeList = width.Select(val => new IndexValuePair() { Index = "width", Value = val }).ToList();
var heightList = height.Select(val => new IndexValuePair() { Index = "height", Value = val }).ToList();
var iteration = 0;
wholeList.ForEach(ivp => { ivp.Index = ivp.Index + count; count = iteration + 1; });
iteration = 0;
heightList.ForEach(ipv => { ivp.Index = ivp.Index + count; count = iteration + 1; });
wholeList.AddRange(heightList);
var sumOfCombinations = GetPowerSet(wholeList).Where(x => x.Count() > 0)
.Select(x => new { Combination = x.ToList(), Sum = x.Sum(ivp => ivp.Value) }).ToList();
StringBuilder sb = new StringBuilder();
sumOfCombinations.ForEach(ivp =>
{
ivp.Combination.ForEach(pair => sb.Append(string.Format("{0} ", pair.Value)));
sb.Append(string.Format("= {0} = ", x.Sum));
ivp.Combination.ForEach(pair=> sb.Append(string.Format("{0} + ", pair.Index)));
sb.Length -= 3;
Console.WriteLine(sb);
sb.Clear();
});
var key = Console.ReadKey();
}

Grouping the elements of an Array with the help of a bin Array

Assume that we have an array:
int[] values = new int[10];
values[0] = 1;
values[1] = 2;
values[2] = 3;
values[3] = 4;
values[4] = 6;
values[5] = 8;
values[6] = 2;
values[7] = 1;
values[8] = 3;
values[9] = 9;
And I have another array, say, the def array that defines the buckets:
int[] def= new int[3]; // defs holds the definition of the buckets
def[0] = 0;
def[1] = 5;
def2] = 10;
I want to use this def array to group the values array, in order to get the frequency distribution, using c#:
i.e.
bin[0] = 7; // the number of array values that lies between 0 and 5
bin[1] = 3; // the number of array values that lies between 5 and 10
I already found a solution with loops, but I am sure there is more elegant and neater way to do this operation; the linq / group by method.
How can I code this procedure using LINQ Group By?
Thanks in advance for anyone contributing to the answer,
Aykut
If I've understood you correctly, then you're looking something like this:
var array = new[] { 1, 2, 3, 4, 6, 8, 2, 1, 3, 9 };
var buckets = new[] { 0, 5, 10 };
var distributionFreq = buckets
.Skip(1) // we don't need the first bucket
.OrderBy(bucket => bucket) // just ensure, that buckets are ordered properly
.Select((bucket, i) => new
{
Min = buckets[i], // minimal value of range
Max = bucket // maximal value of range
})
.Select(range => new
{
Range = range,
NumberOfValuesAtRange = array.Count(item => item > range.Min && item < range.Max)
})
.ToArray();
First, you have to define the range of values (0..5, 5..10, and so on).
Second, count the number of values in source array, which fits the range.
Note, that you should define more precisely the criteria for the outermost values, e.g. does the value of 5 fits the first range, or the second one?
Try this:
var bin =
array
.GroupBy(x => x / 5)
.Select(x => x.Count())
.ToArray();
Or better yet this:
var lookup = array.ToLookup(x => x / 5);
var bin =
Enumerable
.Range(0, lookup.Max(x => x.Key) + 1)
.Select(x => lookup[x].Count())
.ToArray();
This second example works if there are some outlying numbers in the original array.
Or even better, using buckets:
var buckets = new [] { 0, 5, 10, };
var lookup = array.ToLookup(x => buckets.Where(b => x >= b).Count() - 1);
var bin =
Enumerable
.Range(0, lookup.Max(x => x.Key) + 1)
.Select(x => lookup[x].Count())
.ToArray();

How to unifiy two arrays in a dictionary?

If you have two arrays string[] a and int[] b how can you get a Dictionary<string,int> from it most efficiently and with least code possible? Assume that they contain the same number of elements.
For example, is this the best way?
Dictionary<string,int> vals = new Dictionary<string,int>();
for(int i = 0; i < size; i++)
{
vals.Add(a[i],b[i]);
}
If your goal is to match at positions within the sequences, you can use Enumerable.Zip.
int[] myInts = { 1, 2 };
string[] myStrings = { "foo", "bar"};
var dictionary = myStrings.Zip(myInts, (s, i) => new { s, i })
.ToDictionary(item => item.s, item => item.i);
And since you are working with arrays, writing it "longhand" really isn't all that long. However, you want to validate beforehand the arrays truly are equal in length.
var dictionary = new Dictionary<string, int>();
for (int index = 0; index < myInts.Length; index++)
{
dictionary.Add(myStrings[index], myInts[index]);
}
Usually, Linq can result in more expressive, easier to understand code. In this case, it's arguable the opposite is true.
If this is .Net 4, then you can do the following:
var result = a.Zip(b, (first, second) => new {first, second})
.ToDictionary(val => val.first, val => val.second);
Without Zip, you can also do this:
var result = Enumerable.Range(0, a.Length).ToDictionary(i => a[i], i => b[i]);
Using ToDictionary:
int idx = 0;
var dict = b.ToDictionary(d => a[idx++]);
var result = a.ToDictionary(x => x, x => b[a.IndexOf(x)]);

C#: Altering values for every item in an array

I'm wondering if there is built-in .NET functionality to change each value in an array based on the result of a provided delegate. For example, if I had an array {1,2,3} and a delegate that returns the square of each value, I would like to be able to run a method that takes the array and delegate, and returns {1,4,9}. Does anything like this exist already?
LINQ provides support for projections using the Select extension method:
var numbers = new[] {1, 2, 3};
var squares = numbers.Select(i => i*i).ToArray();
You can also use the slightly less fluent Array.ConvertAll method:
var squares = Array.ConvertAll(numbers, i => i*i);
Jagged arrays can be processed by nesting the projections:
var numbers = new[] {new[] {1, 2}, new[] {3, 4}};
var squares = numbers.Select(i => i.Select(j => j*j).ToArray()).ToArray();
Multidimensional arrays are a little more complex. I've written the following extension method which projects every element in a multidimensional array no matter what its rank.
static Array ConvertAll<TSource, TResult>(this Array source,
Converter<TSource, TResult> projection)
{
if (!typeof (TSource).IsAssignableFrom(source.GetType().GetElementType()))
{
throw new ArgumentException();
}
var dims = Enumerable.Range(0, source.Rank)
.Select(dim => new {lower = source.GetLowerBound(dim),
upper = source.GetUpperBound(dim)});
var result = Array.CreateInstance(typeof (TResult),
dims.Select(dim => 1 + dim.upper - dim.lower).ToArray(),
dims.Select(dim => dim.lower).ToArray());
var indices = dims
.Select(dim => Enumerable.Range(dim.lower, 1 + dim.upper - dim.lower))
.Aggregate(
(IEnumerable<IEnumerable<int>>) null,
(total, current) => total != null
? total.SelectMany(
item => current,
(existing, item) => existing.Concat(new[] {item}))
: current.Select(item => (IEnumerable<int>) new[] {item}))
.Select(index => index.ToArray());
foreach (var index in indices)
{
var value = (TSource) source.GetValue(index);
result.SetValue(projection(value), index);
}
return result;
}
The above method can be tested with an array of rank 3 as follows:
var source = new int[2,3,4];
for (var i = source.GetLowerBound(0); i <= source.GetUpperBound(0); i++)
for (var j = source.GetLowerBound(1); j <= source.GetUpperBound(1); j++)
for (var k = source.GetLowerBound(2); k <= source.GetUpperBound(2); k++)
source[i, j, k] = i*100 + j*10 + k;
var result = (int[,,]) source.ConvertAll<int, int>(i => i*i);
for (var i = source.GetLowerBound(0); i <= source.GetUpperBound(0); i++)
for (var j = source.GetLowerBound(1); j <= source.GetUpperBound(1); j++)
for (var k = source.GetLowerBound(2); k <= source.GetUpperBound(2); k++)
{
var value = source[i, j, k];
Debug.Assert(result[i, j, k] == value*value);
}
Not that I'm aware of (replacing each element rather than converting to a new array or sequence), but it's incredibly easy to write:
public static void ConvertInPlace<T>(this IList<T> source, Func<T, T> projection)
{
for (int i = 0; i < source.Count; i++)
{
source[i] = projection(source[i]);
}
}
Use:
int[] values = { 1, 2, 3 };
values.ConvertInPlace(x => x * x);
Of course if you don't really need to change the existing array, the other answers posted using Select would be more functional. Or the existing ConvertAll method from .NET 2:
int[] values = { 1, 2, 3 };
values = Array.ConvertAll(values, x => x * x);
This is all assuming a single-dimensional array. If you want to include rectangular arrays, it gets trickier, particularly if you want to avoid boxing.
Using System.Linq you could do something like:
var newArray = arr.Select(x => myMethod(x)).ToArray();
LINQ queries could easily solve this for you - make sure you're referencing System.Core.dll and have a
using System.Linq;
statement. For example, if you had your array in a variable named numberArray, the following code would give you exactly what you're looking for:
var squares = numberArray.Select(n => n * n).ToArray();
The final "ToArray" call is only needed if you actually need an array, and not an IEnumerable<int>.
you can use linq to accomplish this in shorthand but be careful remember that a foreach occurs underneath anyway.
int[] x = {1,2,3};
x = x.Select(( Y ) => { return Y * Y; }).ToArray();
Here is another solution for M x N arrays, where M and N are not known at compile time.
// credit: https://blogs.msdn.microsoft.com/ericlippert/2010/06/28/computing-a-cartesian-product-with-linq/
public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(IEnumerable<IEnumerable<T>> sequences)
{
IEnumerable<IEnumerable<T>> result = new[] { Enumerable.Empty<T>() };
foreach (var sequence in sequences)
{
// got a warning about different compiler behavior
// accessing sequence in a closure
var s = sequence;
result = result.SelectMany(seq => s, (seq, item) => seq.Concat<T>(new[] { item }));
}
return result;
}
public static void ConvertInPlace(this Array array, Func<object, object> projection)
{
if (array == null)
{
return;
}
// build up the range for each dimension
var dimensions = Enumerable.Range(0, array.Rank).Select(r => Enumerable.Range(0, array.GetLength(r)));
// build up a list of all possible indices
var indexes = EnumerableHelper.CartesianProduct(dimensions).ToArray();
foreach (var index in indexes)
{
var currentIndex = index.ToArray();
array.SetValue(projection(array.GetValue(currentIndex)), currentIndex);
}
}

Categories