0

I am wanting to efficiently convert a multidimensional object array into a list of joined arrays.

Firstly, I have converted the 2D array into a list of arrays:

object[,] data; // This contains all the data.
int count = 0;
List<string[]> dataList = data.Cast<string>()
                          .GroupBy(x => count++ / data.GetLength(1))
                          .Select(g => g.ToArray())
                          .ToList();

And now what I want to do is create a List where I trim and then join all the data in each array. To clarify what I mean, I can do this using:

List<string> dataListCombined = new List<string>();
foreach (string[] s in dataList)
{
    for (int i = 0; i < s.Length; i++)
    {
        s[i] = s[i].Trim();
    }
    dataListCombined.Add(string.Join(",", s));
}

but i just want to know if there is a more efficient way of doing it. Can I alter LINQ Im using above to do it? Thanks

Woody
  • 1,677
  • 7
  • 26
  • 32
  • 1
    Any chance you can show us a sample of the data? It would be helpful, for example, if we could see the data (and the format it's in) along with what you want the data to ultimately look like. – Casey Crookston Sep 18 '19 at 13:49
  • 1
    In addition to my comment above, I'm also wondering why, in your first code sample, you don't convert `data` to a `List>` instead of a `List`. In general, lists are so much easier to work with than arrays. – Casey Crookston Sep 18 '19 at 13:52
  • 2
    Bad idea to put a side-effect into a LINQ predicate. The correct functional programming way is to use a form of _select_ with a predicate that takes an index, and use the index to group - which may also require creating an anonymous object that includes both the index and the original data. It's a bit more work to set up, but this is how predicates are supposed to work as pure functions. I believe side effects in LINQ predicates constitute undefined behaviour. (See the technique in @RashidAli's [answer](https://stackoverflow.com/a/57995295/1563833)) – Wyck Sep 18 '19 at 14:35
  • 1
    This is not an efficient way to return array rows to begin with and using such side effects is a very bad idea - why *group* data when you already know how to access it? Instead of direct access you pay for the division operations, hashing, storing the group data in internal lists and emitting it to the next step. All those string operations generate temporary objects that have to be GC'd adding another layer of inefficiency. – Panagiotis Kanavos Sep 18 '19 at 15:11

2 Answers2

1

Following LINQ code will produce the same results as your complete code in both blocks:

This code will work for a multi dimensional array of any rank (dimensions)

//dimensions in the array
int arrayDimensions = data.Rank;

//create a group of items in each row of the array
//join all items of each group into a comma separated string
//combine all formed strings into a list of strings

List<string> dataListCombined = 
data.Cast<string>().Select((s, i) => new { GroupIndex = i / arrayDimensions, Item = s.Trim()}).
GroupBy(g => g.GroupIndex).Select(g => string.Join(",", g.Select(x => x.Item))).ToList();
Rashid Ali
  • 587
  • 3
  • 13
  • You forgot to assign Item = s, and since OP seems interested in Trimming the data make it s.Trim() – Innat3 Sep 18 '19 at 14:55
  • Thanks. This is the answer but I had to update ```int arrayDimensions = data.Rank;``` to ```int arrayDimensions = data.GetLength(1);``` – Woody Sep 20 '19 at 11:58
0

A trick you can use with LINQ when accessing indexed values is simulating for loops with Enumerable.Range. This isn't more efficient, or even clearer, but does allow for code that indexes easily:

var dataCombined = Enumerable.Range(0, data.GetLength(0))
                             .Select(ri => String.Join(",", Enumerable.Range(0, data.GetLength(1))
                                                                      .Select(ci => data[ri,ci].ToString()
                                                                                               .Trim())))
                             .ToList();
NetMage
  • 26,163
  • 3
  • 34
  • 55