Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

like image 323
Pikachu620 Avatar asked Jun 29 '18 02:06

Pikachu620


2 Answers

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]});
like image 45
MineR Avatar answered Sep 21 '22 23:09

MineR


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.

like image 97
rossipedia Avatar answered Sep 17 '22 23:09

rossipedia