I really love the LINQ language extension in dot net programming and I’m really glad it was added to a language I love (C#).
My background, however, has a lot of C++ in it and I still do some coding in C++ – sometimes for fun and sometimes to put back into practice things I should have know for years, but are just now being recognized by my ever-widening eyes. A lot of that new vision comes from seeing C# and LINQ and, of course, practicing all forms of programming MOJO on programming forums, like DaniWeb.com.
One particular post I saw there was in the C++ forum, where a user posted a question about two-dimensional arrays and how to search for a particular pattern of digits based on array boundaries. The user posted a block of digits like this:
2,2,9,8,7,3,4,
0,0,0,0,0,0,0,
3,4,5,7,8,9,6,
0,0,0,0,0,0,0,
1,3,4,5,5,5,5
The question asked was how to return a 2d array of the rows that did not contain “all” zero values. Most programmers can imagine a series of loops and boolean settings based on boundaries to eliminate the “bad” rows.
I immediately thought “Use a WHERE clause on it”. …then I remembered I was in the C++ forum and, given the experience of the developer, might need a more traditional approach. I gave a suggestion about looping then began to make some examples for myself – one full solution “the (traditional) hard way”, one solution in C# with LINQ and then curiosity took hold and I convinced myself it would not be “too hard” to do it with C++ (CLI) and LINQ. I underestimated the complexity, but consider it good practice.
Here is the C# code:
using System.Collections.Generic;
using System.Linq;
//
namespace DW_409646_CS_CON
{
class Program
{
public static int[,] matrix2d = new int[5, 7]
{
{2,2,9,8,7,3,4},
{0,0,0,0,0,0,0},
{3,4,5,7,8,9,6},
{0,0,0,0,0,0,0},
{1,3,4,5,5,5,5}
};
static void Main(string[] args)
{
List<int> lst_int = matrix2d.OfType<int>().ToList();
//
int intHeight = (matrix2d.GetUpperBound(0) + 1);
int intWidth = (matrix2d.GetUpperBound(1) + 1);
// Method one
int[][] arr =
(
from i in Enumerable.Range(0, intHeight)
let lst_intEachRow = lst_int.GetRange(i * intWidth, intWidth)
where !lst_intEachRow.All(ix => ix.Equals(0))
select lst_intEachRow.ToArray()
).ToArray();
// Method two
int[][] arr2 =
Enumerable.Range(0, intHeight)
.Select(i => lst_int.GetRange(i * intWidth, intWidth)
.ToArray()).Where(xa => !xa.All(ix => ix.Equals(0)))
.ToArray();
}
}
}
The C++ conversion was a little more complex, but can be done. The tricky part was getting the return value from the Enumerable based on the height of the array:
1: #include "stdafx.h"
2: using namespace System;
3: using namespace System::Collections::Generic;
4: using namespace System::Linq;
5: //
6: public ref class CArrayHelper
7: {
8: public:
9: static array<int,2>^ matrix2d;
10:
11: CArrayHelper(array<int,2>^ matrix)
12: {
13: matrix2d = matrix;
14: }
15:
16: static Func<int, String^>^ intToString =
17: gcnew Func<int, String^>(IntToString);
18:
19: static Func<int, bool>^ isZero =
20: gcnew Func<int, bool>(IsZero);
21:
22: static Func<array<int>^, bool>^ notAllZeroes =
23: gcnew Func<array<int>^, bool>(AllZeroes);
24:
25: static Func<int, int, array<int>^>^ getArrFromArr =
26: gcnew Func<int, int, array<int>^>(GetArrFromArr);
27:
28: static array<array<int>^>^ OutputArrayNonZeroes()
29: {
30: return
31: Enumerable::ToArray
32: (Enumerable::Where
33: (Enumerable::Select
34: (Enumerable::Range(0, matrix2d->GetUpperBound(0) + 1),
35: getArrFromArr), notAllZeroes));
36: }
37:
38: private:
39: static String^ IntToString(int i)
40: {
41: return i.ToString();
42: }
43:
44: static bool IsZero(int i)
45: {
46: return i.Equals(0);
47: }
48:
49: static bool AllZeroes(array<int>^ arr)
50: {
51: return !Enumerable::All<int>(arr, isZero);
52: }
53:
54: static array<int>^ GetArrFromArr(int i, int j)
55: {
56: return
57: Enumerable::ToArray(
58: Enumerable::ToList<int>(
59: Enumerable::ToArray(
60: Enumerable::OfType<int>(matrix2d)))
61: ->GetRange(i * (matrix2d->GetUpperBound(1) + 1),
62: (matrix2d->GetUpperBound(1) + 1)));
63: }
64: };
65:
66: void DumpIntArray(array<int>^ arr_int)
67: {
68: Console::WriteLine(
69: String::Join(",",
70: Enumerable::ToArray(
71: Enumerable::Select<int>(
72: Enumerable::ToArray<int>(arr_int), CArrayHelper::intToString))));
73: }
74:
75: int main(array<System::String ^> ^args)
76: {
77: Action<array<int>^>^ dumpIntArray = gcnew Action<array<int>^>(DumpIntArray);
78:
79: array<array<int>^>^ arrOutput = CArrayHelper(gcnew array<int,2>(5,7)
80: {
81: {2,2,9,8,7,3,4},
82: {0,0,0,0,0,0,0},
83: {3,4,5,7,8,9,6},
84: {0,0,0,0,0,0,0},
85: {1,3,4,5,5,5,5}
86: }).OutputArrayNonZeroes();
87:
88: Enumerable::ToList<array<int>^>(arrOutput)->ForEach(dumpIntArray);
89: return 0;
90: }