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

  • We Won The One Award I Actually Care About

    Full Scale made the Inc. 5000 for the fifth year straight, the 12th listing across my three companies. Here is why the one award you cannot buy is worth stopping for.

  • Your Customers Build the Features Now

    I let a tool I liked sit dead for a year rather than build the features I wanted. An MCP server meant I never had to, and your customers can do the same to your product.

  • Get the Size of a Directory in Linux the Easy Way

    du -sh for the quick answer, ncdu for the cleanup, df for the disk itself: every command for checking directory size in Linux, plus why du and df never agree.

  • Vim Search and Replace: The Ultimate Guide

    One :%s command replaces every match in a file before a find dialog would even open. The Vim substitute patterns worth the muscle memory: flags, ranges, capture groups, and multi-file edits.