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