0

I have this JSON string:

{
  "countries": [
    {
      "countryCode": "AR",
      "country": "Argentina"
    },
{
      "countryCode": "BR",
      "country": "Brazil"
    }
  ]
}

and this Country class and countries list:

List<Country> countries { get; set; }
class Country
    {
        public string country { get; set; }
        public string countryCode { get; set; }
    }

I need to create a two-dimensional object array containing the countries code and name:

propVal[0, 0] = "AR";
propVal[0, 1] = "Argentina";
propVal[1, 0] = "BR";
propVal[1, 1] = "Brazil";
.
.
.

Right now I'm "manually" looping through the countries list and build the object array:

int row = 0;
foreach (Country country in countries)
{
    propVal[row, 0] = country.countryCode;
    propVal[row, 1] = country.country;
    row++;
}

The long shot is to have a generic way, applicable to other JSONs, having let's say 3 or more properties and resulting in a x-dimensional object array. Is there a LINQ way to do this? I know about this thread, dealing with one object property and for which the LINQ approach is countries.Select(x=>x.country).ToArray(), but in my case there are multiple properties needed.

Thank you for your help!

adim
  • 129
  • 1
  • 9
  • 3
    This seems like an X/Y problem. why do you need a multi dimensional array, and not a list of class (country) – TheGeneral Nov 03 '20 at 09:26
  • Because the generated multi dimensional object array will be written into an Excel sheet. – adim Nov 03 '20 at 09:46
  • You could probably use reflection to do it. But it won't be pretty... how about using a [DataTable](https://stackoverflow.com/a/36348017/1336590)? – Corak Nov 03 '20 at 09:50
  • There's no built in linq method to generate a multi-dimensional array - and there is a good reason for that: The number of dimensions an array have must be known at compile time - so you can't have a method that returns an array with an arbitrary number of of dimensions. The only way a method can do that is by returning `dynamic` or `object` - and that's an open door for all kind of problems - the main problem being errors that should have been compile time errors are pushed to runtime. – Zohar Peled Nov 03 '20 at 09:52
  • Just serialize it as normal, then loop through to fill out 2d array. also you could get fancy with JObject, but i wouldn't bother. just serialize it as normal – TheGeneral Nov 03 '20 at 09:57

2 Answers2

0

I recommend switching from multidimensional arrays to arrays of arrays—it is waaaaay easier to implement, and will most likely satisfy all of your needs in this task.

Here is what you need to do. Suppose you have some generalized class with a bunch of public properties that you want to serialize this way:

    public class A
    {
        public string P1 {get; set;}
        public string P2 {get; set;}
        public string P3 {get; set;}
    }

You can get the collection of properties of that class like this:

    var props = typeof(A).GetProperties();

And then, all you need to do is LINQ over your collection of items and get the values for each property:

var result = items.Select(item => 
                     props.Select(prop => 
                       prop.GetValue(item)
                           .ToString())
                          .ToArray())
                  .ToArray();

Where items is a List<A>, of course.

And that's it. You have yourself an array of arrays.

If you feel like you definitely need it to be a 2D array, then you'll have to implement the conversion like here.

user4182984
  • 222
  • 2
  • 9
0

If you really need to build a 2D array you can arrange the data dynamically this way:

var data = new List<Country>
{
    new Country {country = "Brazil", countryCode = "BR"},
    new Country {country = "Argentina", countryCode = "AR"}
};

// declare all the property accessors you want from your type (Country)
// it can be built by reflection
var accessors = new List<Func<Country, string>>
{
    x => x.country,
    x => x.countryCode
};

// declaring the 2D array,
//   first dimension length is the size of the data (number of countries)
//   second dimension length is the number of fields from the data type (Country)
var propVal = new string[data.Count, accessors.Count];

// projecting each element with each field accessor (cartesian product)
// that is (countries.Count) x (accessors.Count)
// (0,0,"Brazil") (0,1,"BR") (1,0,"Argentina") (1,1,"AR")
var flatenData = data.SelectMany((country, i) => accessors.Select((f, j) => (i, j, field: f(country))));

// filling the 2D array
foreach (var tuple in flatenData)
{
    propVal[tuple.i, tuple.j] = tuple.field;
}

If we display the 2D array:

// displaying the 2D array
for (var i = 0; i < propVal.GetLength(0); i++)
{
    for (var j = 0; j < propVal.GetLength(1); j++)
    {
        Console.WriteLine($"[{i},{j}] = {propVal[i,j]}");
    }
}

/*
[0,0] = Brazil
[0,1] = BR
[1,0] = Argentina
[1,1] = AR
*/
polkduran
  • 2,533
  • 24
  • 34