-2

i have the following simplified input:

List<string[]> sList = new List<string[]>();
sList.Add(new string[]{ "Product", "ProductID"});
sList.Add(new string[] { "Fork", "1" });
sList.Add(new string[] { "Spoon", "2" });
sList.Add(new string[] { "Knife", "3" });

and i want the following output

ResultList[0] == { "Product" ,"Fork","Spoon","Knife"};
ResultList[1] == { "ProductID" ,"1","2","3"};

first i solved this with 2 loops, then i changed my approach to this linq (both working):

List<string[]> Result = sList.SelectMany(x => x)
  .Select((x, index) => new KeyValuePair<int, string>(index % sList.First().Length, x))
  .GroupBy(s => s.Key).Select(g => g.Select(x => x.Value)
  .ToArray()).ToList();

it works but it seems to be a bit circuitous to me. isn't there a simpler approach (built in?) i've overlooked?

stuartd
  • 70,509
  • 14
  • 132
  • 163
fubo
  • 44,811
  • 17
  • 103
  • 137
  • what result do you expect for *jagged data* like that: `new {new String[] {"1", "2"}, new String[] {"A"}, new String[] {"3", "4", "5"}};`? How do you want to represent a *hole*, i.e. "2" *hole* "4"? – Dmitry Bychenko Aug 19 '15 at 11:36
  • @DmitryBychenko - the column count of all rows is always equal - you can rely on – fubo Aug 19 '15 at 11:37
  • 2
    Try this: `var result = new List { sList.Select(x => x[0]).ToArray(), sList.Select(x => x[1]).ToArray() };` – Mitat Koyuncu Aug 19 '15 at 11:39
  • 1
    The operation you're looking for is usually called *transpose*. See [this example](http://stackoverflow.com/a/2070434/857807). – dcastro Aug 19 '15 at 11:47

3 Answers3

2

There's a better solution for this: use Dictionary<TKey, TValue>.

Dictionary<string, string> products = new Dictionary<string, string>()
{
     { "Fork", "1" },
     { "Spoon", "2" }
};

Now you can access products.Keys (which will get product names) and products.Values (which will get product identifiers):

ResultList[0] = new [] { "Product" }.Union(products.Values);
ResultList[1] = new [] { "ProductID" }.Union(products.Keys);

Possible automatization:

List<List<string>> resultList = new [] { "Product", "ProductID" }
                    .Select((g, index) => (index == 0 ? new [] { g }.Union(products.Keys) : new [] { g }.Union(products.Values)).ToList())
                    .ToList();

BTW, as long as you've stored your data in a dictionary, I don't find a reason to add "column names" to the result if you know that product names are products.Keys and identifiers products.Values. That is, you can work with the dictionary directly...

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
1

What about something like this:

List<string[]> sList = new List<string[]>();
sList.Add(new string[] { "Product", "ProductID" });
sList.Add(new string[] { "Fork", "1" });
sList.Add(new string[] { "Spoon", "2" });
sList.Add(new string[] { "Knife", "3" });

var res = new List<string[]> { sList.Select(a => a[0]).ToArray(), sList.Select(b => b[1]).ToArray() };
Amir Popovich
  • 29,350
  • 9
  • 53
  • 99
1

So if you know for sure that each array contains exactly 2 elements, problem becomes a combination of two trivial selecs. However if you only know that all arrays are of the same size, but do not want to rely on exact numbers, this approach should do it:

List<string[]> Result =         
    sList.First()
         .Select((x, i) => i)
         .Select(i => sList.Select(x => x[i]).ToArray())
         .ToList();

We are basically using first item in sList to figure out how many members there are in the array, and then doing a select for each of them.

Andrei
  • 55,890
  • 9
  • 87
  • 108
  • i upvoted all 3 answers because i havn't thought of any of them. i accepted this seems to be the most generic to me. – fubo Aug 19 '15 at 13:28