3

Consider 2D array like this one:

enter image description here

With the following code the empty rows are skipped:

public static string[,] SkipBlankRows(this string[,] array2D)
{
    var columns = array2D.GetLength(1);
    var rows = array2D.GetLength(0);
    var temp = new List<string[]>();

    for (var r = 0; r < rows; r++)
    {
        var row = new string[columns];
        for (var c = 0; c < columns; c++)
        {
            row[c] = array2D[r, c];
        }
        if (row.All(itm => string.IsNullOrWhiteSpace(itm)))
            continue;
        temp.Add(row);
    }

    string[,] result = new string[temp.Count(), columns];
    rows = temp.Count();

    for (int r = 0; r < rows; r++)
    {
        var row = temp[r];
        for (var c = 0; c < row.Length; c++)
        {
            result[r,c]=row[c];
        }
    }
    return result;
}

Usage:

void Main()
{
    var x = new string[,] { { "", "", "" }, { "", "X", "" }, { "X", "X", "X" }, { "", "", "" }, {"X","","X"}, {"X","X","X"}};
    var y = x.SkipBlankRows();
}

Result:

The result should be 2D-array of string where the blank rows will not be there.

enter image description here

The code looks awkward to me, is it possible to do it better e.g. to involve linq?

glick
  • 102
  • 6

3 Answers3

2

You could use LINQ to get the string[,] to an IEnumerable<IEnumerable<string>> with the empty rows removed, then put the IEnumerable<IEnumerable<string>> back into a string[,]. I'm not aware of any way with LINQ to project an IEnumerable<IEnumerable<string>> into a string[,], so I just used nested foreach loops.

public static string[,] SkipBlankRows(this string[,] array2D)
{
    int columnCount = array2D.GetLength(1);

    var withoutEmptyLines = array2D
        .Cast<string>()  // Flatten the 2D array to an IEnumerable<string>
        .Select((str, idx) => new { str, idx }) // Select the string with its index
        .GroupBy(obj => obj.idx / columnCount) // Group the items into groups of "columnCount" items
        .Select(grp => grp.Select(obj => obj.str)) // Select the groups into an IEnumerable<IEnumerable<string>>
        .Where(strs => !strs.All(str => string.IsNullOrWhiteSpace(str))); // Filter empty rows;

    // Put the IEnumerable<IEnumerable<string>> into a string[,].
    var result = new string[withoutEmptyLines.Count(), columnCount];
    int rowIdx = 0;
    foreach (var row in withoutEmptyLines)
    {
        int colIdx = 0;
        foreach (var col in row)
        {
            result[rowIdx, colIdx++] = col;
        }
        rowIdx++;
    }

    return result;
}
Joshua Robinson
  • 3,399
  • 7
  • 22
  • 1
    Thank you for your answer, interesting linq usage! I will probably stay with my original code to preserve readability, but good to see how linq is powerful. – glick Feb 21 '20 at 10:29
2

LINQ was intended to process and produce collections rather than multidimensional arrays. You can replace the first for loop with some LINQ that's a little more expressive, but you can't really get away from using for loops for repopulating the new array:

public static string[,] SkipBlankRows(this string[,] array2D)
{
    var columns = array2D.GetLength(1);
    var rows = array2D.GetLength(0);
    var temp = Enumerable.Range(0, rows)
        .Select(i => Enumerable.Range(0, columns).Select(j => array2D[i, j]).ToList())
        .Where(row => !row.All(string.IsNullOrEmpty))
        .ToList();

    string[,] result = new string[temp.Count, columns];
    rows = temp.Count;

    for (int r = 0; r < rows; r++)
    {
        var row = temp[r];
        for (var c = 0; c < row.Count; c++)
        {
            result[r, c] = row[c];
        }
    }
    return result;
}

Of course, if you're willing to bring in a couple of helper methods to abstract away the conversion to and from rows, you can end up with a highly efficient and very easy-to-read code.

StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • 1
    Thank you for your answer, interesting linq usage! I will probably stay with my original code to preserve readability, but good to see how linq is powerful. – glick Feb 21 '20 at 10:30
1

It depends on what you want your output to look like, do you just want to skip over the blank strings and have a list of values? Or do you want your data to still be in the multi-dimentional array?

If your answer to this question is "the second one" then your code is fine.

If you just want a list of all of the values out of the multi-dimentional array you could write something like:

public static IEnumerable<string> SkipBlankRows(this string[,] array2D)
{
    return (from string s in array2D where !string.IsNullOrWhiteSpace(s) select s);
}

This returns a flat structure of values from the array.

Hope this helps

Mark Davies
  • 1,447
  • 1
  • 15
  • 30
  • Thanks for your answer! I want just to remove (skip) blank rows and return the `2D array of string`. – glick Feb 20 '20 at 15:29