Implementing LINQ’s Enumerable methods in C++ with GCC and CLion for Linux

Hi Community,

Today’s post is about my personal implementation of some Enumerable methods available in .NET but written in C++.  My implementation is pretty generic, because it uses a template class that returns a std::vector<T> as if it were IEnumerable<T>. C++ has always provided templates to allow developers write generic code; even when they might look and seem similar to generics , they are not the same. I had posted about their differences here.

LinqCore class

//

// Created by angel on 01/02/17.

//

 

#ifndef NATIVELINQ_LINQCORE_H

#define NATIVELINQ_LINQCORE_H

 

#include "CommonHeaders.h"

 

// Enumerable methods

// https://msdn.microsoft.com/en-us/library/system.linq.enumerable_methods(v=vs.110).aspx

 

template <class T>

class LinqCore {

public:

    /*

     * Filters a sequence of values based on a predicate

     */

    static std::vector<T> Where(std::vector<T> collection, std::function<bool(T)> condition) {

        std::vector<T> retval;

 

        if (collection.size() > 0 && condition != nullptr) {

            for (auto &p : collection) {

                if (condition(p))

                    retval.push_back(p);

            }

        }

 

        return retval;

    }

 

    /*

     * Returns a specified number of contiguous elements from the start of a sequence

     */

    static std::vector<T> Take(std::vector<T> collection, int count, std::function<bool(T)> condition) {

        auto index = 0;

        std::vector<T> retval;

        count = count < 0 ? 1 : count;

 

        if (collection.size() > 0 && condition != nullptr) {

            for (auto &p : collection) {

                if (condition(p)) {

                    retval.push_back(p);

                    if (++index == count)

                        break;

                }

            }

        }

 

        return retval;

    }

 

    /*

     * Returns a number that represents how many elements in the specified sequence satisfy a condition

     */

    static int Count(std::vector<T> collection, std::function<bool(T)> condition) {

        auto retval = 0;

 

        if (collection.size() > 0 && condition != nullptr) {

            for (auto &p : collection) {

                if (condition(p))

                    retval++;

            }

 

            return retval;

        }

    }

 

    /*

     * Computes the average of a sequence of Decimal or Float values

     */

    static T Average(std::vector<T> collection) {

        long length = 0;

        T retval = 0, sum = 0;

        auto type = std::string(typeid(T).name());

 

        // we process float and double only

        if (type == "f" || type == "d" ) {

            if ((length =  collection.size()) > 0) {

                for (auto &p : collection) {

                    sum += p;

                }

                retval = sum / length;

            }

        }

 

        return retval;

    }

 

    /*

     * Casts the elements of an array to a std::vector

     */

    static std::vector<T> Cast(T source[], int size) {

        std::vector<T> retval;

 

        try {

            for (auto index = 0; index < size; index++)

                retval.push_back(source[index]);

        } catch(std::exception &ex) {

            // just to handle any undefined behavior (e.g.: such as bad::alloc)

        }

 

        return retval;

    }

 

    /*

     * Returns the maximum value in a sequence of Float, Decimal or Integer values

     */

    static T Max(std::vector<T> collection) {

        T retval = 0;

        auto type = std::string(typeid(T).name());

        auto comparison = [&](T a, T b) -> bool {return a < b; };

 

        // we process float, double and integer only

        if (type == "f" || type == "d" || type == "i") {

            retval = *std::max_element(collection.begin(), collection.end(), comparison);

        }

 

        return retval;

    }

 

    /*

     * Returns the minimum value in a sequence of Float, Decimal or Integer values

     */

    static T Min(std::vector<T> collection) {

        T retval = 0;

        auto type = std::string(typeid(T).name());

        auto comparison = [&](T a, T b) -> bool {return a < b; };

 

        // we process float, double and integer only

        if (type == "f" || type == "d" || type == "i") {

            retval = *std::min_element(collection.begin(), collection.end(), comparison);

        }

 

   /*

     * Computes the sum of a sequence of Float, Decimal or Integer values.

     */

    static T Sum(std::vector<T> collection) {

        T retval = 0;

        auto type = std::string(typeid(T).name());

 

        // we process float, double and integer only

        if (type == "f" || type == "d" || type == "i") {

            if (collection.size() > 0) {

                for (auto &p : collection) {

                    retval += p;

                }

            }

        }

 

        return retval;

    }

};

 

 

#endif //NATIVELINQ_LINQCORE_H

          

 

Demo application

//

// Created by angel on 01/02/17.

//

 

 

#include "CommonHeaders.h"

#include "Contact.h"

#include "LinqCore.h"

 

int main() {

    // We set up some test data

    int numbersArray[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

 

    std::vector<float> grades {12.75, 14.32, 16.76, 18.22};

 

    std::vector<Contact> contacts {Contact(1, "John", "Doe", "01/01/1985", "john.doe@gmail.com"),

                                   Contact(2, "Matt", "Roberts", "07/07/1975", "mattr@hotmail.com"),

                                   Contact(3, "Jane", "Doe", "07/10/1980", "janed@hotmail.com")};

 

    Contact contactsArray[]  = {Contact(1, "Joe", "Bloggs", "10/10/1980", "joe@bloggs.com"),

                                Contact(2, "Karly", "Smith", "05/05/1980", "karlys@gmail.com"),

                                Contact(3, "Michael", "White", "05/05/1980", "michael@gmail.com")};

 

    // Where method

    auto where = LinqCore<Contact>::Where(contacts, [&](Contact c) -> bool {return c.LastName == "Doe";});

 

    // Average method

    auto average = LinqCore<float>::Average(grades);

 

    // Sum method

    auto sum = LinqCore<float>::Sum(grades);

 

    // Cast into std::vector<Contact>

    auto castContact = LinqCore<Contact>::Cast(contactsArray, 3);

 

    // Cast into std::vector<int>

    auto castInt = LinqCore<int>::Cast(numbersArray, 10);

 

    // Count method

    auto count = LinqCore<Contact>::Count(contacts, [&](Contact c) -> bool {return c.LastName == "Doe";});

 

    // Max method

    auto max = LinqCore<int>::Max(castInt);

 

    // Min method

    auto min = LinqCore<int>::Min(castInt);

 

    getchar();

 

    return 0;

}

 

Source code is here.

I hope you find it useful.

Regards,

Angel

Leave a Reply

Your email address will not be published. Required fields are marked *