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 aTas 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 aTand returns aT
linq::Linqable<T> linq::Linqable<T>::from(It begin, It end): A static method of the classLinqablethat uses two iterators in order to build theLinqableobjectlinq::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 towhereusually used after the first (and supposedly only)wherein 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).wbis 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 ofnelements (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 typeReturnTypebased 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 ofTelements 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_listis 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 xis equal torefusingoperator==PREDICATE isNot(T ref): Returns a predicate that is true only when a givenT xis not equal torefusingoperator!=PREDICATE isGreaterThan(T ref): Returns a predicate that is true only when a givenT xis greater thanrefusingoperator>PREDICATE isLessThan(T ref): Returns a predicate that is true only when a givenT xis less thanrefusingoperator<PREDICATE isGreaterThanOrEqualTo(T ref): Returns a predicate that is true only when a givenT xis greater than or equal torefusingoperator>=PREDICATE isLessThanOrEqualTo(T ref): Returns a predicate that is true only when a givenT xis less than or equal torefusingoperator<=
PREDICATE e(T ref): Alias ofis(added solely for consistency)PREDICATE ne(T ref): Equivalent toisNotbut usingoperator==PREDICATE lt(T ref): Alias ofisLessThan(added solely for consistency)PREDICATE gt(T ref): Equivalent toisGreaterThanbut usingoperator<PREDICATE le(T ref): Equivalent toisLessThanOrEqualTobut usingoperator<PREDICATE ge(T ref): Equivalent toisGreaterThanOrEqualTobut 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 0In 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 DESCOr in C# :
from elem in arr
where (elem%2==0 || elem==1) && true
orderby elem descending
select elemAs 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