If you're interested in using or contributing to this library, or if you just have questions, I'd really like to hear from you! Contact me at timothy.shields@live.com.
The IEnumerable<T>
interface and associated LINQ extension methods provided by the .NET framework enable programmers to write concise, fluent, and composable query expressions using powerful abstractions.
linq-cpp brings equivalent functionality to the C++11 environment - and in a way that's "done right."
Users of .NET LINQ and the IEnumerable<T>
interface should find linq-cpp immediately familiar. The precedent set by the .NET IEnumerable<T>
and IEnumerator<T>
interfaces and IEnumerable<T>
extension methods have been mimicked directly.
The methods section has a list of all of the functions available in linq-cpp. Any .NET LINQ methods that aren't yet available should be added soon!
linq-cpp is written to be cross-platform and fully compliant with the C++11 standard. It has no dependencies outside of the C++11 standard library. It can be compiled and built using any of the following.
- Visual Studio 2012 (Plan to upgrade to Visual Studio 2013 because of new C++11 compiler feature support.)
- gcc 4.6
- clang 3.1
build system: CMake
linq-cpp is configured to build using the CMake build system. For other projects already using CMake, integrating linq-cpp as a dependency is a simple procedure.
continuous integration: Travis CI
linq-cpp is built automatically by Travis CI. Each time this GitHub repository updates, Travis CI clones and builds the repository using the Linux compilers it provides (see the portability section for the list of compilers). The Windows compilers (namely Visual Studio 2012) have to be run manually outside of Travis.
(coming soon) Automated testing using Travis CI
The Travis configuration file is .travis.yml.
linq-cpp is licensed under the Apache License, Version 2.0. You can view the license file here.
The linq-cpp library is built on top of two foundation concepts, Enumerable<T>
and Enumerator<T>
, mimicking the IEnumerable<T>
and IEnumerator<T>
interfaces from .NET, respectively. The definitions of these two concepts follow.
A type Type
meets the requirements of Enumerable<T>
if it meets all of the following requirements.
Type
meets the MoveConstructible requirementsType
meets the MoveAssignable requirementsType::enumerator_type
is a type that meets theEnumerator<T>
requirementsType::value_type
is the typeT
enumerator_type Type::get_enumerator()
is a member function
The Enumerable<T>
concept prescribes the following usage pattern. Usage not following this pattern yields undefined behavior.
- Construct instance
E
of a type meeting the requirements ofEnumerable<T>
- Construct, use, and destruct zero or more instances of a type meeting the requirements of
Enumerator<T>
- Construct using
E.get_enumerator()
- Use according to the
Enumerator<T>
usage pattern
- Destruct
E
Important to note here is that the an Enumerator<T>
must always be destructed before its parent Enumerable<T>
is destructed.
A type Type
meets the requirements of Enumerator<T>
if it meets all of the following requirements.
Type
meets the MoveConstructible requirementsType
meets the MoveAssignable requirementsType::value_type
is the typeT
bool Type::move_first()
is a member functionbool Type::move_next()
is a member functionvalue_type Type::current()
is a member function
The Enumerator<T>
concept prescribes the following usage pattern. Usage not following this pattern yields undefined behavior.
- Construct instance
E
of a type meeting the requirements ofEnumerator<T>
- Optionally call
E.move_first()
E.move_first()
is not called: proceed to (4)E.move_first()
is called and it returnsfalse
: proceed to (4)E.move_first()
is called and it returnstrue
: proceed to (3)
- Call
E.current()
zero or more times; proceed to (2) - Destruct
E
Below this point has not yet been updated from v1
Suppose you have the following types.
enum class Genders
Male
Female
class Employee
const std::string& FirstName() const
const std::string& LastName() const
int Age() const
Genders Gender() const
class Customer
int ID() const
class Department
const std::vector<Employee*>& Employees() const
const std::vector<Customer*>& Customers() const
You're given vector<Department*> departments
, int customerID
, and the following task.
- Get the employees younger than 21 who work in departments servicing the customer with the given ID.
- The results should be grouped by age and gender, and within each group the employee data should be sorted by last name then first name.
- An employee may work in multiple departments, but the results shouldn't contain any employee more than once.
linq-cpp makes this complex task straightforward.
vector<Department*> departments = ...;
int customerID = ...;
// Could use "auto" to have the compiler determine the query's return type
// but an explicit breakdown makes it clearer.
typedef tuple<int, Genders> EmployeeGroupKey;
typedef pair<EmployeeGroupKey, vector<Employee*>> EmployeeGroup;
vector<EmployeeGroup> results =
Enumerable::FromRange(departments)
.Where([=](Department* d)
{
return Enumerable::FromRange(d->Customers()).Any([=](Customer* c){ return c->ID == customerID; });
})
.SelectMany([](Department* d){ return Enumerable::FromRange(d->Employees()); })
.Where([](Employee* e){ return e->Age() < 21; })
.Distinct()
.GroupBy([](Employee* e){ return make_tuple(e->Age(), e->Gender()); })
.Select([](pair<tuple<int, Genders>, TEnumerable<Employee*>> group)
{
return std::make_pair(
group.first,
group.second
.OrderBy([](Employee* e){ return make_tuple(e->LastName(), e->FirstName()); })
.ToVector());
})
.ToVector();
TEnumerable<T> FromRange(TRange& range)
TEnumerable<T> FromRange(std::shared_ptr<TRange> range)
- Constructs an enumerable from an STL range
TEnumerable<T> Factory(TFactory factory)
TFactory = TEnumerable<T>()
- Repeated calls to
factory
argument each create an enumerable that can only be enumerated once - Result is enumerable that can be enumerated as many times as desired
TEnumerable<T> Repeat(T item)
- Represents an infinite sequence of which every element is
item
TEnumerable<T> Empty()
- Represents an empty sequence
TEnumerable<T> Return(T item)
- Represents a sequence that contains a single element
item
TEnumerable<T> Generate(TFactory factory)
- Repeated calls to factory generate elements of sequence
TEnumerable<T> Sequence(T start, TPredicate predicate, TNext next)
TEnumerable<T> Sequence(T start, TNext next)
TEnumerable<T> Sequence(T start)
TEnumerable<T> Sequence()
TPredicate = bool()
TNext = T(T)
- The sequence generated by a typical
for
loop TEnumerable<T> Range(T start, T count)
- The sequence
start, start+1, ..., start+count
TEnumerable<T> Concat(TEnumerable<T> first, TEnumerable<T> second)
- Concatenates two sequences
TEnumerable<TResult> Zip(TEnumerable<T1> first, TEnumerable<T2> second, TSelector selector)
TSelector = TResult(T1, T2)
- Merges two sequences by using the specified
selector
function
TEnumerable<TResult> Select(TSelector selector)
TSelector = TResult(T)
- Projects each element of a sequence into a new form
TEnumerable<TResult> StaticCast()
- Static casts the elements of a sequence to the specified type
TEnumerable<TResult> DynamicCast()
- Dynamic casts the elements of a sequence to the specified type
TEnumerable<TResult> SelectMany(TSelector selector)
TSelector = TEnumerable<TResult>(T)
- Projects each element of a sequence to a new sequence and flattens the resulting sequences into one sequence
T SelectMany()
- Only available when
T = TEnumerable<S>
- Concatenates a sequence of sequences
TEnumerable<T> Where(TPredicate predicate)
TPredicate = bool(T)
- Filters a sequence of values based on a predicate
TEnumerable<std::pair<T, int>> Index()
- Projects each element
x
of a sequence to the pair(x, i)
wherei
is the index ofx
TEnumerable<TResult> SelectIndexed(TSelector selector)
TSelector = TResult(T, int)
- Projects each element of a sequence into a new form by incorporating the element's index
TEnumerable<T> WhereIndexed(TPredicate predicate)
TPredicate = bool(T, int)
- Filters a sequence of values based on a predicate incorporating the element's index
TEnumerable<T> Skip(int count)
- Bypasses a specified number of elements in a sequence and then returns the remaining elements
TEnumerable<T> SkipWhile(TPredicate predicate)
TPredicate = bool(T)
- Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements
TEnumerable<T> SkipWhileIndexed(TPredicate predicate)
- TODO
TEnumerable<T> Take(int count)
- Returns a specified number of contiguous elements from the start of a sequence
TEnumerable<T> TakeWhile(TPredicate predicate)
TPredicate = bool(T, int)
- Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements
TEnumerable<T> TakeWhileIndexed(TPredicate predicate)
- TODO
TEnumerable<T> ToInclusive(T end)
- Take elements while they are less than or equal to end
TEnumerable<T> ToExclusive(T end)
- Take elements while they are less than end
TEnumerable<T> Do(TAction action)
TAction = void(T)
- Inject a side effect of enumerating
TEnumerable<T> DoIndexed(TAction action)
TAction = void(T,int)
- Inject a side effect of enumerating
TEnumerable<T> Order(TComparer comparer)
TComparer = int(T, T)
- Sorts the elements of a sequence in ascending order using the given comparer
TEnumerable<T> Order()
- Sorts the elements of a sequence in ascending order using the default comparer
TEnumerable<T> OrderBy(TKeySelector keySelector)
TKeySelector = TKey(T)
- Sorts the elements of a sequence in ascending order with respect to selected keys
TEnumerable<std::pair<TKey, TEnumerable<T>> GroupBy(TKeySelector keySelector)
TKeySelector = TKey(T)
- Groups the elements of a sequence by selected keys
- The groups are returned in ascending order with respect to selected keys
bool Any()
- Determines whether a sequence contains any elements
bool Any(TPredicate predicate)
- Determines whether any element of a sequence satisfies a condition
bool Contains(T item)
bool All(TPredicate predicate)
- Determines whether all elements of a sequence satisfy a condition
T First()
T First(TPredicate predicate)
T Last()
T Last(TPredicate predicate)
T ElementAt(int i)
T Single()
T Single(TPredicate predicate)
int Count()
int Count(TPredicate predicate)
TAccumulate Aggregate(TAccumulate seed, TAccumulator accumulator)
TAccumulator = TAccumulate(TAccumulate, T)
T Aggregate(TAccumulator accumulator)
TAccumulator = T(T, T)
T Sum()
T Product()
double Average()
TResult Min(TSelector selector)
T Min()
TSelector = TResult(T)
TResult Max(TSelector selector)
T Max()
TSelector = TResult(T)
T MinBy(TKeySelector keySelector)
TKeySelector = TKey(T)
T MaxBy(TKeySelector keySelector)
TKeySelector = TKey(T)
int MinIndex(TKeySelector keySelector)
int MinIndex()
TKeySelector = TKey(T)
int MaxIndex(TKeySelector keySelector)
int MaxIndex()
TKeySelector = TKey(T)
void ForEach(TAction action)
TAction = void(T)
void ForEachIndexed(TAction action)
TAction = void(T, int)
void IntoVector(std::vector<T>& _vector)
std::vector<T> ToVector()
void IntoSet(std::set<T>& _set)
std::set<T> ToSet()
void IntoMap(std::map<TKey, TValue>& _map, TKeySelector keySelector, TValueSelector valueSelector)
void IntoMap(std::map<TKey, T>& _map, TKeySelector keySelector)
void IntoMap(std::map<TKey, TValue>& _map)
TKeySelector = TKey(T)
TValueSelector = TValue(T)
std::map<TKey, TValue> ToMap(TKeySelector keySelector, TValueSelector valueSelector)
std::map<TKey, T> ToMap(TKeySelector keySelector)
std::map<TKey, TValue> ToMap()
TKeySelector = TKey(T)
TValueSelector = TValue(T)
void IntoLookup(std::map<TKey, std::shared_ptr<std::vector<T>>>& _map, TKeySelector keySelector)
TKeySelector = TKey(T)
std::map<TKey, std::shared_ptr<std::vector<T>>> ToLookup(TKeySelector keySelector)
TKeySelector = TKey(T)
std::string ToString(std::string separator, TWriter writer)
std::string ToString(std::string separator)
std::string ToString()
TWriter = void(std::stringstream&, T)