Skip to content
Jason Db edited this page Jun 19, 2015 · 44 revisions

Project Description

JLinq.ts(.js) is an implementation of .Net's Linq To Objects. It is built using iterators for complete lazy evaluation to reduce memory and improve performance.

Why I Built This Library

I wanted to learn TypeScript along with building a linq library which had method names and syntax much like the .Net version. I love underscore but I always had to lookup the method name I was looking for.

Features
  • Implement's .Net Linq methods with the same syntax
  • Complete lazy evaluation to reduce memory footprint while improving performance
  • You can use the Typescript file for type safety or use the .js version for plain old Javascript.
  • Drastically outperforms Underscore.js in Chrome, Firefox, and Safari

Basic Example

declare my array that I will represent my data source
var _Array= [];

let's throw some sample data into my array
for (var i = 0; i < 10; i++)
{
_Array.push({ id: i, txt: i.toString(), subArray: [1,2,3] });
}

Let's run a sample where I filter any record where Id > 5. The return value will be a iterator.
var myQuery = _Array.Where(function (x) { return x.id > 5; });

How do I get my results from my query above?

  • Call .ToArray() which will evaluate the results right away and return an array from the where clause
    var results = _Array.Where(function (x) { return x.id > 5; }).ToArray();

  • I could also run though the results 1 at a time. This would reduce memory because we never have to materialize a large array when calling ToArray().
    Holds the current item of the result set
    var CurrentResult;

Checks the query to see if we have any more records that are returned from the query while ((CurrentResult = QueryToRun.Next()).CurrentStatus !== ToracTechnologies.JLinq.IteratorStatus.Completed)
{
do something with my result
var myResultItem = CurrentResult.CurrentItem;
}


Chaining Methods
You can chain query methods together. No data will be evaluated until ToArray() or the .Next() method is called. This method when ToArray() or .Next() is called would return an array of ints. (id is an int off of my object in the collection)
var myQuery = _Array.Where(function (x) { return x.id > 5; }).Select(function(x){ return { x.id;});

Or I could split my queries such as the following:
build the start of my query
var myQuery = _Array.Where(function (x) { return x.id > 5; })

if i have some boolean i want to check.
if (OnlyTakeTop5Items)
{
//go sort the items and only take 5 items
var myResults= myQuery.OrderBy(function(x){ return x.id; }).Take(5).ToArray();
}


Methods
Below are the list of the rest of the methods we have in the library

  • Where(function(T): boolean): Query(T)
    Description: Will only return items in the collection where the predicate evaluates to true.
    Example: var myQuery = _Array.Where(function (x) { return x.id > 5; }).ToArray();

  • FirstOrDefault(function(T): boolean): Nullable(T)
    Description: Returns the first item where the predicate evaluates to true. If no items evaluate to true then a null value will be returned.
    Notes: You can call FirstOrDefault() (with no function) to get the first item (null if not found)
    Example: var myFirstItem = _Array.FirstOrDefault(function (x) { return x.id == 5; });

  • First(function(T): boolean): T - (Error If T Not Found)
    Description: Returns the first item where the predicate evaluates to true. If no item evaluates to true then an error is raised.
    Notes: You can call First() (with no function) to get the first item (error if not found)
    Example: var myFirstItem = _Array.First(function (x) { return x.id == 5; });

  • Select(function(T): any): Query(any)
    Description: Returns the result of object after it has been passed into the function. Used to transform T.
    Example: var myQuery = _Array.Select(function (x) { return { newId: x.id+1}; }).ToArray();

  • SelectMany(function(T): Array(TProperty) [Property Of Collection To Flatten]): Query(TProperty)
    Description: This will select every subArray property. Mainly used to flatten out sub properties.
    Example:

Sample Data
Object 1 = { id: 1, lst: [1,2,3]};
Object 2 = { id: 2, lst: [4,5,6]};

Syntax
var myQuery = _Array.SelectMany(function (x) { return x.lst;}).ToArray();

Result Of Query
Results: [1,2,3,4,5,6]

  • Take(number): Query(T)
    Description: Only returns the x number of elements where the predicate evaluates to true.
    Example: var my2Items= _Array.Where(function (x) { return x.id > 5; }).Take(2).ToArray();

  • Skip(number): Query(T)
    Description: Skips over the first x number of elements where the predicate evaluates to true.
    Example: var myItems = _Array.Where(function (x) { return x.id > 5; }).Skip(2).ToArray();

  • Count(): integer
    Description: Returns the number of elements.
    Example: var countOfItems= _Array.Where(function (x) { return x.id > 5; }).Count();

  • Count(function(T): boolean): integer
    Description: Returns the number of elements found where the predicate evaluates to true.
    Example: var countOfItems= _Array.Count(function(x) { return x.id > 10;});

  • All(function(T): boolean): boolean
    Description: If all the items evaluate to true then true will be returned.
    Example: var myItems = _Array.All(function (x) { return x.id > 5; });

  • Any(function(T): boolean): boolean
    Description: Returns true if 1 item evaluates to true.
    Note: You don't have to pass in a function. This will return if the collection or query has at least 1 element.
    Example: var hasItem = _Array.Where(function (x) { return x.id > 5; }).Any(function(x){ return x.id < 50; });

  • Last(function(T): boolean): T
    Description: Returns the last item in the collection that evaluates to true.
    Note: You don't have to pass in a function. This will just return the last item in the collection / query.
    Example: var lastItem = _Array.Last(function(x){ return x.id < 9; });

  • Distinct(function(T) : TProperty(any)): Array(TProperty)
    Description: Returns a list of the distinct values of the property passed in
    Example: var distinctIds = _Array.Distinct(function(x){ return x.id; }).ToArray();

  • Min(): number
    Description: Returns the min number in the collection.
    Example: var minId = _Array.Select(returns the min number in the collection function(x){ return x.id; }).Min();

  • Max(): number
    Description: Returns the max number in the collection.
    Example: var maxId = _Array.Select(function(x){return x.id;}).Max();

  • Sum(): number
    Description: Returns the sum of the numbers in the collection.
    Example: var sumOfIds = _Array.Select(function(x){ return x.id; }).Sum();

  • Average(): number
    Description: Returns the average of the numbers in the collection.
    Example: var avgerageOfIds = _Array.Select(function(x){ return x.id; }).Average();

  • GroupBy(function(T): TGroupByField(any)) : Query(TGroupByField, T)
    Description Returns an array of Keys with an array of items in that key.
    Example: var groupedData = _Array.GroupBy(function(x){ return x.id; });

  • OrderBy(function(T): TSortByProperty(any)) : Query(T)
    Description: Returns a sorted array by the property passed in.
    Example: var sortedData = _Array.Where(function (x) { return x.id > 5; }).OrderBy(Function(x){ return x.id; });

  • OrderByDescending(function(T): TSortByProperty(any)) : Query(T)
    Description: Returns a desc sorted array by the property passed in.
    Example: var sortedData = _Array.Where(function (x) { return x.id > 5; }).OrderByDescending(Function(x) { return x.id; });

Multi Property Sorting
You can use multiple fields to sort (additional sort columns) by adding the
OrderBy(...).ThenBy(function(x){ return x.Id2;})


Or to secondary sort by desc then use:
OrderBy(...).ThenByDescending(function(x){ return x.Id2;})


You can keep tacking on additional sort columns:
OrderBy(...).ThenBy(function(x){ return x.Id2;}).ThenBy(function(x){return x.Id3;});


  • Paginate(function(T) CurrentPageNumber: number, HowManyRecordsPerPage: number): Array(T)
    Description: Pages the data and only returns the current page of data. Example: var myPagedData= _Array.Paginate(1, 100);

  • SingleOrDefault(function(T): boolean): T?
    Description: First Item If Predicate Found, If Multiple Items Are Found Then Error Is Raised. If Nothing Is Found Then Null Is Returned
    Note: You can call SingleOrDefault() (with no function) to get the single item (error if not found)
    Example: var myFirstItem = _Array.SingleOrDefault(function (x) { return x.id == 5; });

  • Single(function(T): boolean): T (multiple items found will raise an error)
    Description: First Item If Predicate Found, If Multiple Items Are Found Then Error Is Raised. If Nothing Is Found Then Error Is Raised.
    Note: You can call Single() (with no function) to get the single item (error if not found)
    Example: var myFirstItem = _Array.Single(function (x) { return x.id == 5; });

  • Concat(function(T): Array(T): Query(T)
    Description: Combines both sets of data into one dataset.
    Note: Concat does not removes duplicate values
    Example: var myConcatResult = _Array.Concat(_AnotherArray);
    or
    Example 2: var myConcatResult = _Array.Where(x => x.Id == 1).Concat(_AnotherArray);

  • ConcatQuery(function(T): Iterator(T)): Query(T)
    Description: Combines both sets of data into one dataset.
    Note: Concat does not removes duplicate values
    Example: var myConcatQuery = _Array.ConcatQuery(_AnotherArray.Where(x => x.Id == 1));
    or
    Example2: var myConcatQuery = _Array.Where(x => x.Id == 1).ConcatQuery(_AnotherArray.Where(x => x.Id == 1));

  • Union(function(T): Array(T)): Query(T)
    Description: Combines both sets of data into one dataset. Removes any duplicate items.
    Example: var myUnionResult = _Array.Union(_AnotherArray);
    or
    Example2: var myUnionResult = _Array.Where(x => x.Id == 1).Union(_AnotherArray);

  • UnionQuery(function(T): Iterator(T)): Query(T)
    Description: Combines both sets of data into one dataset. Removes any duplicate items.
    Example: var myUnionResult = _Array.UnionQuery(_AnotherArray.Where(x => x.Id == 1));
    or
    Example2: var myUnionResult = _Array.Where(x => x.Id == 1).UnionQuery(_AnotherArray.Where(x => x.Id == 1));

  • SkipWhile(function(T): boolean): Query(T)
    Description: Will Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements. "Where" will return everything that meet the condition. SkipWhile will find the first element where the condition is met, and return the rest of the elements.
    Example: var mySkipWhileResult= _Array.SkipWhile(function(x){ return x.id > 2; });

  • TakeWhile(function(T): boolean): Query(T)
    Description: Will return all the elements before the test no longer passes. "Where" will return everything that meet the condition. TakeWhile will exit the routine wasn't it doesn't pass the expression
    Example: var myTakeWhileResult= _Array.TakeWhile(function(x){ return x.id > 2; });

  • Aggregate(function(WorkingT: T, CurrentT: T): T): Query(T)
    Description: creates a running total "T" then passes it in for each element
    Example: var QueryToAggregate: Array<number> = [1, 2, 3, 4];

//go build the query
`` var ResultOfQuery = QueryToAggregate.Where(x => x > 2).Aggregate((WorkingT, CurrentT) => WorkingT + CurrentT);`


AsQueryable

If I have a scenario where I might have a filter / may not. I can declare the array AsQueryable. Using AsQueryable() will simplify the syntax.

ie: var query = List.AsQueryable();

if (filter1 != null)
{
query = query.Where(function(x .....))
}

if (filter2 != null)
{
query = query.Where(function(x......))
}

//grab the results
var results = query.ToArray();


Other Collection Types


Dictionaries
Instead of calling .ToArray() you can push the results to a dictionary. The function passed into the ToDictionary is the dictionary key selector.
var myDictionary = _Array.Where(function (x) { return x.id > 5; }).ToDictionary(function(x){ return x.id;});

A dictionary has the following methods
* ContainsKey(key value to test) - Returns true / false if the key value exists in the dictionary
* Count - Returns int. How many items exist in the dictionary
* GetItem(key value to get) - Returns the value of the dictionary if its exists. Returns null if we can't find the item in the dictionary.
* Keys() - Gets all the keys in the dictionary and returns them. Array Of Keys
* Values() - Gets all the values in the dictionary and returns them. Array Of Values
* GetAllItems() - Gets all the key / value pairs in the dictionary. Array Of TKey and TValue (Key value pair)

Additional Dictionary Information
Dictionaries support a composite key ie: { Id: x.Id, Id2: x.Id2 };
var myDictionary = _Array.Where(function (x) { return x.id > 5; }).ToDictionary(function(x){ return { Id: x.Id, Id2: x.Id2;});


Hash Sets Instead of pushing things to an array or a dictionary you can push it to a hashset. This uses an object behind the scenes for fast item lookup.

    Push the results to a hashset
    _Array.Where(x => x.Id == 1).ToHashSet();

Methods

* ContainsItem(ItemToRetrieve: TValue): boolean - Tries to find the key / object in the hashset

* Add(ValueToAdd: TValue): boolean - Add an item to the hashset. Will return true if it was added. Returns false if it already exists in the hashset

* Values(): Array<TValue>- Gets all the values in the hashset

* Remove(KeyToRemove: TValue) -Removes an item

* BuildHashSet(HashSetDataSource: Iterator<TValue> - Builds a hashset from a Iterator, so if we have an iterator we don't need to materialize that AND a hashset and a key selector

* Count(): number - Gets the count of items in the hashset
Clone this wiki locally