LINQ++ is a (headers only) C++11 library that aims to emulate the behavior of LINQ in other languages such as C#.
Collection manipulation in C++ is often painful, LINQ++ makes it way easier.
These are the features that are currently supported :
-
linq::Linqable<T>::value_type
: The type of the elements of thisLinqable
-
linq::Linqable<T>::reference
: Equivalent toT&
-
linq::Linqable<T>::ref
: Alias for the above -
linq::Linqable<T>::const_reference
: Equivalent toconst T&
-
linq::Linqable<T>::const_ref
: Equivalent to the above -
linq::Linqable<T>::pointer
: Equivalent toT*
-
linq::Linqable<T>::ptr
: An alias forlinq::Linqable<T>::pointer
-
linq::Linqable<T>::const_pointr
: Equivalent toconst T*
-
linq::Linqable<T>::const_ptr
: An alias forlinq::Linqable<T>::const_ptr
-
linq::Linqable<T>::container
: The type of container used by theLinqable
-
linq::Linqable<T>::iterator
: The type of iterators to aLinqable
-
linq::Linqable<T>::const_iterator
: The type of iterators (const-aware) to aLinqable
-
linq::Linqable<T>::Predicate
: A predicate that takes aT
as argument -
linq::Linqable<T>::WhereBuilder
: A function that takes alinq::Linqable<T>
and returns alinq::Linqable<T>
-
linq::Linqable<T>::SelfMapper
: A function that takes aT
and returns aT
linq::Linqable<T> linq::Linqable<T>::from(It begin, It end)
: A static method of the classLinqable
that uses two iterators in order to build theLinqable
objectlinq::Linqable<T> linq::from<T>(It begin, It end)
: A utilitary function that calls the above, it is basically syntactic sugar
linq::Linqable<T> linq::Linqable<T>::where(Predicate p)
: The method usually used to start the selection sequence (chaining WHERE operations), only the elements that match the predicatep
(T => bool
) will be keptlinq::Linqable<T> linq::Linqable<T>::orWhere(linq::Linqable<T>::Predicate p)
: Adds elements from the previous selection that match the given predicatelinq::Linqable<T> linq::Linqable<T>::andWhere(Predicate p)
: An alias towhere
usually used after the first (and supposedly only)where
in order to add an additionnal filter to the current selectionlinq::Linqable<T> linq::Linqable<T>::andComplexWhere(WhereBuilder wb)
: Used to make selection sub-operations (for instance, inWHERE a AND (b OR C)
,(b OR C)
would usually require a sub-operation).wb
is a function that isLinqable<T> => Linqable<T>
linq::Linqable<T> linq::Linqable<T>::limit(unsigned int n)
: Restricts the current selection to a maximal amount ofn
elements (selects them in their order)linq::Linqable<T> linq::Linqable<T>::unique()
: Removes duplicates from the current selection (packs to set and goes back toLinqable
)
linq::Linqable<T> linq::Linqable<T>::orderDesc()
: Sorts the currently selected elements in descending order using theoperator>
linq::Linqable<T> linq::Linqable<T>::orderAsc()
: Sorts the currently selected elements in ascending order using theoperator<
linq::Linqable<T> linq::Linqable<T>::orderDescBy(Transformer f)
: Sorts the currently selected elements in ascending order using the transformed elements's (viaf
,T => E
)operator>
linq::Linqable<T> linq::Linqable<T>::orderAscBy(Transformer f)
: Sorts the currently selected elements in ascending order using the transformed elements's (viaf
,T => E
)operator<
linq::Linqable<T> linq::Linqable<T>::select()
: Returns*this
, only used for semantic coherence purposeslinq::Linqable<ReturnType> linq::Linqable<T>::select(Mapper mapper)
: Maps the currently selected elements using the given mapper functionmapper
(T => ReturnType
) and render them available as the current selectionlinq::Linqable<T> linq::Linqable<T>::selet(Mapper mapper)
: Maps the currently selected elements to elements of the same type and render them available as the current selectionReturnType linq::Linqable<T>::selectReduced(Reducer red, ReturnType acc)
: Reduces the currently selected elements to a single value of the typeReturnType
based upon the reducer functionred
((ReturnType, T) => ReturnType
) and the inital value of the accumulatoracc
I would describe the packing operation as the operation of getting to a usual C++ Container (or pointer) from a Linqable
object : Linqable
serving only as a way to manipulate your base Container easily.
T* linq::Linqable<T>::packToPointer()
: Allocates a pointer ofT
elements from aLinqable<T>
std::vector<T> linq::Linqable<T>::packToVector()
: Creates astd::vector<T>
from the currently selected elements of theLinqable<T>
objectstd::list<T> linq::Linqable<T>::packToList()
: Creates astd::list<T>
from the currently selected elements of theLinqable<T>
objectstd::set<T> linq::Linqable<T>::packToSet()
: Creates astd::set<T>
from the currently selected elements of theLinqable<T>
objectstd::deque<T> linq::Linqable<T>::packToDeque()
: Creates astd::deque<T>
from the currently selected elements of theLinqable<T>
objectstd::forward_list<T> linq::Linqable<T>::packToForwardList()
: Creates astd::forward_list<T>
from the currently selected elements of theLinqable<T>
object. Note that this operation has a bit more overhead than the others due to the fact that insertion instd::forward_list
is done to the front, so we have to reverse the list to keep the original order
LINQ++ comes with a bit of utilitaries function that makes it easier to use (even though some may only be used for the development of this library, eg. TransformerGreaterThanComparator
).
The base namespace for those is linq::utils
.
Identity<T>
: A struct that is used as a functor for the identity function ((T a) => a
)identityOf<T>()
: A function that returns the identity function of the given typeT
Most of those functions are meant to ease the use of WHERE operations by giving support to recurrent operations (With x being the element : x==n
,x<n
, n<x<m
, x%2==0
, etc...).
The base namespace for those is linq::utils::helperFunctions
.
Let's have PREDICATE
designate a function T => bool
and let's have T
be the type of the elements in the Linqable<T>
object.
PREDICATE is(T ref)
: Returns a predicate that is true only when a givenT x
is equal toref
usingoperator==
PREDICATE isNot(T ref)
: Returns a predicate that is true only when a givenT x
is not equal toref
usingoperator!=
PREDICATE isGreaterThan(T ref)
: Returns a predicate that is true only when a givenT x
is greater thanref
usingoperator>
PREDICATE isLessThan(T ref)
: Returns a predicate that is true only when a givenT x
is less thanref
usingoperator<
PREDICATE isGreaterThanOrEqualTo(T ref)
: Returns a predicate that is true only when a givenT x
is greater than or equal toref
usingoperator>=
PREDICATE isLessThanOrEqualTo(T ref)
: Returns a predicate that is true only when a givenT x
is less than or equal toref
usingoperator<=
PREDICATE e(T ref)
: Alias ofis
(added solely for consistency)PREDICATE ne(T ref)
: Equivalent toisNot
but usingoperator==
PREDICATE lt(T ref)
: Alias ofisLessThan
(added solely for consistency)PREDICATE gt(T ref)
: Equivalent toisGreaterThan
but usingoperator<
PREDICATE le(T ref)
: Equivalent toisLessThanOrEqualTo
but usingoperator<
PREDICATE ge(T ref)
: Equivalent toisGreaterThanOrEqualTo
but usingoperator<
-
PREDICATE isBetween(T lhs, T rhs)
: Equivalent to$elem \in [lhs ; rhs]$ -
PREDICATE isInRange(T lhs, T rhs)
: Equivalent to$elem \in [lhs ; rhs[ $ -
PREDICATE isWithin(T lhs, T rhs)
: Equivalent to$elem \in \space]lhs;rhs[ $ -
PREDICATE isInExclusiveRange(T lhs, T rhs)
: Equivalent to$elem \in \space]lhs ; rhs] $
bool isOdd(T elem)
bool isEven(T elem)
As we already know, collection manipulation in C++ is not exactly as smooth as it can be in other languages.
LINQ++ tends to simplify collection manipulation operation by offering various ways to operate on a container.
The way you would usually process is to pass the begin
and end
iterator of your C++ Container as the parameters of linq::from
(do not forget to specify the type of elements, eg. linq::from<int>(arr.begin(), arr.end())
) and then chain operations until you are willing to pack
them back to a C++ Container.
Let's have an example, shall we ?
#include "linq/linq.hpp"
#include <set>
#include <forward_list>
using namespace std;
using namespace linq::utils::helperFunctions;
set<int> arr = {0,1,2,3,4,5,6,7,8,9};
forward_list<int> linqed = linq::from<int>(arr.begin(), arr.end())
->where(isEven<int>) //I want even numbers only
->orWhere(is(1)) //But if there are any 1s, keep them
->andWhere([](const int& elem)->bool{ return true; }) //Then keep everything from that
->orderDesc() //Order them in descending order
->select() //Optional
->packToForwardList(); //Pack them into a forward_list
for(int elem : linqed)
cout << elem << " ";
//prints: 8 6 4 2 1 0
In SQL, that would roughly be equivalent to (if we have a table arr
that has elem
as a column) :
SELECT elem
FROM arr
WHERE ((MOD(elem, 2)=0 OR elem=1) AND TRUE)
ORDER BY elem DESC
Or in C# :
from elem in arr
where (elem%2==0 || elem==1) && true
orderby elem descending
select elem
As of v1.0.2, you can use member functions in order to ease your job at manipulating collections of objects :
#include <string>
using namespace std;
using namespace linq::utils::helperFunctions;
class RandomPelo{
protected:
string name;
bool lives;
public:
Employe(string name, bool lives = true) : name{name}, lives{lives}{}
bool isAlive(){ return this->lives; }
string getName(){ return this->name; }
};
vector<RandomPelo> vect = {};
bool isAlive = true;
for(char c='a' ; c <= 'z' ; c+=1) {
vect.push_back( RandomPelo{string{c}, isAlive} );
isAlive = !isAlive;
}
string names = linq::from<RandomPelo>(vect.begin(), vect.end())
->where(&RandomPelo::isAlive) //I only want to keep those who are alive
->select<string>(&RandomPelo::getName) //I want to retrieve their name
->select<string>([](string s){ return s + " "; }) //I want to add a blank space after their name
->selectReduced<string>(reducers::strings::concat, string{}); //I want to concatenate their name