Linq Challenge in C++

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: }

This article is part of the GWB Archives. Original Author: Tom Hines

New on Geeks with Blogs