Hugoware.net

jLinq is a fully extensible Javascript library that allows you to perform LINQ style queries on arrays of object.

Try jLinq Online!

Try out the jLinq demo below to see if you like it before you download it!

Get Involved

jLinq is on CodePlex so you can contribute ideas, code or documentation!

Get A Quick Start

Watch a screencast to get up to speed on using jLinq quickly!
  1. jLinq Basics
  2. Extending Methods in jLinq
  3. Modifying Live Data In A Query
  4. jLinq 2.2.1 (Minor Update)

Writing Your Query

First, make sure your query starts with an assignment, for example...
var results = jLinq.from(data.users)
    .is("admin")
    .greater("age", 25)
    .orderBy("-age") //the '-' means descending
    .select();

This will make sure there is something to display. If you don't add an assignment, the query will attempt to add it in for you, but it could fail (and that wouldn't be any fun).

What "Data" Do I Use For My Tests?

There are already sets of records you loaded onto this demo page. You can view a list of the available arrays of records.

Available Sets Of Records

  • data.users - A sample list of user accounts and information.
  • data.locations - A sample list of locations, which match up with the locationId for data.users.
  • data.fruit - An array of regular ol' strings.
  • data.numbers - An array of simple numbers (all integers).

So, as shown before, you can use the Javascript var results = jLinq.from(data.nameOfRecords)... to start selecting records.

If You Get Stuck

If you are having problems getting the query to work then check out the jLinq samples on the right side for examples. Most of the samples have a button to show you the query in action.

Extending jLinq

If you are trying to extend jLinq on this page, remember that anything you add to the jLinq library will not be available once you leave the page.

Did you create an extension you think should be added to the core jLinq library? Did you create an entirely new jLinq library that you want to share? Visit the Message Boards to share your extensions!

jLinq is a simple way to query arrays of data in Javascript. Imagine you had a medium sized set of JSON data. You could write some custom code to select the information you wanted, or you could use jLinq to query your data like the example below.

Simple jLinq Query
var results = jLinq.from(data.users)
    .startsWith("first", "a")
    .or("j").or("m")
    .contains("permissions", "write")
    .orderBy("age")
    .select();
            

jLinq examines the query provided and then returns the records that meet the criteria provided!

Operators are mandatory when writing queries. jLinq comes with standard operators such as or(), and() and not().

Simple jLinq Query
var results = jLinq.from(data.users)
    .startsWith("first", "a").or("b").or("c")
    .orderBy("first")
    .select();
            

Each time you use a command, another requirement is added to your query. You'll notice that unless you use or(), the operator and() will be implied!

You probably also noticed that the query above doesn't repeat the command name or field name. You can learn more about that in the Command Memorizing section.

Combining Operators

Let's say we wanted a list of all administrators and all non-admins with delete permissions. If you run the following query you'll find that we aren't going to get any results.

Simple jLinq Query
//This query is the WRONG way to do this
var results = jLinq.from(data.users)
    .is("admin")
    .isNot("admin")
    .contains("permissions", "delete")
    .select();
            

Why? Because the entire query is being evaluated together. We need a way to combine sections of our query to be evaluated separately. To do this, we use the combine() or orCombine() command.

Simple jLinq Query
var results = jLinq.from(data.users)    
    .is("admin")
    .orCombine(function(q) {
        q.isNot("admin")
         .contains("permissions", "delete");
    })
    .orderBy("admin")
    .select();
            

Much better. The ability to combine sections of our query gives us much greater control over the results.

Look at the query below. Does it appear to be a little... redundant?

Simple jLinq Query
//This is the WRONG way to query
var results = jLinq.from(data.users)
    .startsWith("first", "a").or()
    .startsWith("first", "b").or()
    .startsWith("first", "c").or()
    .startsWith("first", "d").or()
    .startsWith("first", "e")
    .orderBy("first")
    .select();
            

Command Memorizing

jLinq uses Command Memorizing to improve queries. Each time you use a field name it is memorized to the query. Each time you use a command, that command is memorized as well.

Simple jLinq Query
//A better way to do the previous
var results = jLinq.from(data.users)
    .startsWith("first", "a")
    .or("b").or("c").or("d").or("e")
    .orderBy("-first") // '-' means descending
    .select();
    
//hint: you can also do the following...
//.startsWith('first', ['a','b','c','d','e'])
            

Remember Across Methods

When you use a command and do not supply the field name, the query attempts to use the previous field. This even works across different commands. Below is a more complicated example.

Simple jLinq Query
var results = jLinq.from(data.users)
    .startsWith("first", "a") //sets the field name
    .or("b") // reuses the field and command
    .endsWith("y") // reuses the field
    .orCombine(function(q) {
        //remembers inside a combine 
        q.startsWith("m").endsWith("n");
    })
    .orderBy("first")
    .select();
            

Command Memorizing allows you to write shorter and clearer queries.

jLinq makes it easy to work with more than one set of data as well. You can join different sets of data using the join() command.

Simple jLinq Query
var results = jLinq.from(data.users)
    .join(data.locations, //the source array
        "location", //the alias to use (when joined)
        "locationId", // the location id for the user
        "id" // the id for the location
    )
    .select(function(r) {
        return {
            fullname:r.first + " " + r.last,
            city:r.location.city,
            state:r.location.state
        };
    });
            

Accessing Nested Properties

By joining records you have access to their nested properties. You can even use those properties in queries. For example...

Simple jLinq Query
var results = jLinq.from(data.users)
    .join(data.locations, "location", "locationId", "id")
    .equals("location.state", "texas")
    .orderBy("location.city")
    .select(function(r) {
        return {
            fullname:r.first + " " + r.last,
            city:r.location.city,
            state:r.location.state
        };
    });
            

jLinq makes it easy to query members of a record, even those that aren't just simple data type. For example, you can use an index for an array.

Simple jLinq Query
var results = jLinq.from(data.users)
    .equals("permissions[1]", "write")
    .select();
            

Accessing Methods

jLinq will also allow you to access methods that are part of the record. For example...

Simple jLinq Query
var results = jLinq.from([
    { id:4, getId:function() { return this.id; }},
    { id:8, getId:function() { return this.id; }}
    ])
    .notGreater("getId()", 5)
    .select();
            

Joined Data

If you join a set of records, you can access those properties just as you normally would. For example...

Simple jLinq Query
var results = jLinq.from(data.users)
    .join(data.locations, "location", "locationId", "id")
    .startsWith("location.city", "a")
    .select(function(r) {
        return { first:r.first, city: r.location.city };
    });
            

This allows easy access to your data regardless of the format.

jLinq extensions are very powerful. This section is a summary of what you can do, but you will want to read the documentation for full details.

jLinq was designed with extension methods in mind. The entire jLinq library is built by using the extension framework! By using this framework, user generated extension methods will still with with existing query features such as operators and Command Memorizing.

You can lear more about extending jLinq in the documentation section.

Extension Examples

Below are a few examples of some extension methods. This first example doesn't accept any additional parameters from the user. It still expects a field name unless one was already provided from before.

Simple jLinq Query
//Extend the command
jLinq.extend({
  name:"startsWithLetterP", 
  type:"query", 
  count:0, 
  method:function(q) {
      return q.helper.match(q.value, /^p/);
  }});

//use the new method       
var results = jLinq.from(data.users)
    .startsWithLetterP("first")
    .select();
            

Accepting Parameters

This second example accepts a single parameter from a user. You'll also notice that the method used for this command accepts the query (q) and the value provided by the user (value). The q parameter (query) is a helper object with information about the current query.

Simple jLinq Query
//Extend the command
jLinq.extend({
  name:"equalsWhenDividedByTwo", 
  type:"query", 
  count:1, 
  method:function(q, value) {
      return q.helper.equals((q.value / 2), value);
  }});

//use the new method       
var results = jLinq.from(data.users)
    .equalsWhenDividedByTwo("age", 16)
    .select();
            

Custom Namespaces

Extending a command also allows you to supply a namespace for your methods. Some libraries, for example the base jLinq library, does not allow you to override non-custom methods. This example shows how you could write your own startsWith() method.

Simple jLinq Query
//Extend the command
jLinq.extend({
  name:"startsWith", 
  type:"query", 
  count:1,
  namespace:"custom", //error without namespace
  method:function(q, value) {
      return q.helper.equals(q.value.substr(0, 1), value);
  }});

//use the new method       
var results = jLinq.from(data.users)
    .custom.startsWith("first", "c")
    .select();
            

Fits In Automatically!

Lastly, as stated before, extension methods automatically work with the existing jLinq framework. Operators, Command and Field Memorizing, method generation, it all works as expected. For example...

Simple jLinq Query
//Extend the command
jLinq.extend({
  name:"oldPeople", 
  type:"query", 
  count:0,
  method:function(q, value) {
      return q.value > 50;
  }});

//use the new method       
var results = jLinq.from(data.users)
    .notEquals("age", 10)
      .andNot(20).andNot(30).andNot(40)
    .notOldPeople() //no field required, uses 'not'
    .orderBy("-age") // '-' means descending
    .select();
            

Below is a list of commands available in jLinq. Extension methods that are added while you're using the demo will not be listed here.

Remember, the parameter count does NOT INCLUDE the field name. This is the number required for the command to work. Because field names can be remembered, and in a way are optional, they are not considered to be required.

This list was sorted, grouped and generated using jLinq (check out the source code!)

Handling Case Comparisons

String comparisons are not case sensitive by default. (Most string comparisons are done using Regular Expressions).

Simple jLinq Query
var results = jLinq.from(data.users)
    //.ignoreCase() <-- no need, enabled by default
    .equals("gender", "F") //returns 13 records
    .select();
            

If you want to use case sensitivity, use the method useCase().

Simple jLinq Query
var results = jLinq.from(data.users)
    .useCase()
    .equals("gender", "F") //returns 2 records
    .select();
            

jLinq has several simple string related comparisons such as startsWith(), endsWith() and contains(). (These commands, however, also work with other types which you may want to refer to the documentation section).

Simple jLinq Query
var results = jLinq.from(data.users)
    .startsWith("first", "a") //first letter is 'a'?
    .orEndsWith("on") // last two letters are 'on'?
    .orContains("r") // has an 'R' anywhere?
    .orderBy("first")
    .select();
            

You can also simplify string comparisons using a regular expression with the match() command.

Simple jLinq Query
var results = jLinq.from(data.users)
    //match() obeys ignore/use case
    .match("first", /^(m|p)|l{2}/) 
    .orderBy("first")
    .select();
            

Often when you're selecting information, you also want to change how the information is displayed. Using the orderBy() command, you can select the way you want to order the information.

Simple jLinq Query
var results = jLinq.from(data.users)
    .orderBy("first")
    .select();
            

jLinq supports for up to 25 different sorting orders. To use more criteria for your sorting just add them as another parameter.

Simple jLinq Query
var results = jLinq.from(data.users)
    .orderBy("admin", "age") //first by admin, then by age
    .select();
            

You must do all of your ordering in the same order by command. Ordering is done immediately, not when you make a selection.

You can sort descending by appending a '-' to the front of the field name.

Simple jLinq Query
var results = jLinq.from(data.users)
    .orderBy("-age") //descending
    .select();
            

Grouping Data

You can group records based on a provided key using the groupBy command. The groupBy command returns a new jLinq query that has an array of records with a key (the value of the field) and items (the array of each group).

Simple jLinq Query
var results = jLinq.from(data.users)
    .groupBy("admin") //group admins and non-admins
    .select(function(r) {
      return {
        state:(r.key?"Is Admin":"Is NOT Admin"),
        people:jLinq.from(r.items)
            .orderBy("first")
            .select(function(p) { return p.first; })
      };
    });
            

Distinct Records

The distinct command returns all unique records for the given field, but only returns the values (not the rest of the record). Distinct checks for case differences even if you have flagged your query to ignore case.

Simple jLinq Query
var results = jLinq.from(data.users)
    .distinct("gender")
            

This demo includes a sample data file that you can test with. Below are examples of what that data looks like. Make sure to include the data. prefix before the name of the set you want to query (for example jLinq.from(data.users)...etc...

data.users

data.users contains sample information that contains a lot of common information. It also includes a locationId that can be joined with data.locations.

Simple jLinq Query
//data.users record sample
{ 
    first: "Haley", 
    last: "Baker", 
    age: 14, 
    admin: true, 
    locationId: 3, 
    gender: "f", 
    lastLogin: new Date("9/28/2007 1:01 AM"), 
    permissions: ["read", "write", "delete"] 
}            
            

data.locations

data.locations contains a list of general cities and names, each with their own unique id. It also includes a reference to Abilene, Texas... better known as The Most Boring City On Earth.

Simple jLinq Query
//data.locations record sample
{ 
    id: 1, 
    city:"Abilene", 
    state:"Texas" 
}           
            

data.fruits

data.fruits is a simple string array without any properties, each with a name of a fruit.

Simple jLinq Query
//data.fruits record sample
[ 
    "apple", 
    "orange", 
    "bannana"
    //... etc...
]
            

data.numbers

data.numbers is similar to the data.fruits array in that is contains a list of numbers without any properties.

Simple jLinq Query
//data.numbers record sample
[ 
    2, 
    99, 
    123
    //... etc...
]
            

jLinq examines the query provided and then returns the records that meet the criteria provided!

jLinq now includes several new commands for aggregate functions.

The sum command takes a set of data and creates the sum from the value. If you are using a number field, it is the sum of the values. Other kinds of fields attempt to find a number (like a .length property) to add together.

Using the sum command
var set = jLinq.from(data.users)
    .startsWith("first", "a")
    .orderBy("age");

//display the results and show the sum
var results = set.select();
alert("sum age:" + set.sum("age").result + 
    " -- sum first name length: " + set.sum("first").result);
            

The average works the same as the sum command, but returns the average value of the fields that were added together.

Using the average command
var set = jLinq.from(data.users)
    .startsWith("first", "a")
    .orderBy("age");

//display the results and show the sum
var results = set.select();
alert("avg age:" + set.average("age").result + 
    " -- avg first name length: " + set.average("first").result);
            

The min and max commands work by finding the smallest and largest value for the field.

Using the min and max commands
var set = jLinq.from(data.users)
    .startsWith("first", "a")
    .orderBy("age");

//display the results and show the sum
var results = set.select();
alert("min age:" + set.min("age") + 
    " -- max age:" +set.max("age"));
            

The skipWhile skips records until the function you provide returns a true value. The delegate is passed the record itself so you can compare for values.

Using the skipWhile command
var results = jLinq.from(data.users)
    .orderBy("age")
    .skipWhile(function(r) { return r.age < 50; });
            

Much like the previous command, the takeWhile commands selects records until the function provided returns a false.

Using the takeWhile command
var results = jLinq.from(data.users)
    .orderBy("age")
    .takeWhile(function(r) { return r.age < 50; });
            

The except command allows you to evaluate two separate arrays and then filter the non-matching results.

Using the except command
//If you're older than 60, sorry!
var oldPeople = jLinq.from(data.users)
    .greater("age", 60)
    .select();
            
var results = jLinq.from(data.users)
    .startsWith("first", "a")
    .except(oldPeople) //<-- Filters out Abby
    .select();

//full list 
alert("All Old People\n" + jLinq.from(oldPeople)
    .select(function(r) { return r.first + ":" + r.age; })
    .join("\n"));
            

Intersect works like the previous command except that it returns only the matches that are found both in the original query and the comparison list.

Using the intersect command
//If you're older than 60, sorry!
var oldPeople = jLinq.from(data.users)
    .greater("age", 60)
    .select();
            
var results = jLinq.from(data.users)
    .startsWith("first", "a")
    .intersect(oldPeople) //<-- Only has Abby
    .select();

//full list 
alert("All Old People\n" + jLinq.from(oldPeople)
    .select(function(r) { return r.first + ":" + r.age; })
    .join("\n"));
            

Union works by creating a new array of items that are unique to both arrays.

Using the union command
//If you're older than 60, sorry!
var oldPeople = jLinq.from(data.users)
    .greater("age", 60)
    .select();
            
var results = jLinq.from(data.users)
    .startsWith("first", "a")
    .union(oldPeople) //All people, no duplicates
    .select();

//full list 
alert("All Old People\n" + jLinq.from(oldPeople)
    .select(function(r) { return r.first + ":" + r.age; })
    .join("\n"));
            

The reverse command switches the order of all records in a query. Typically you would use orderBy to change the order of your records, but this allows you to change the order of a set of records without needing to sort on the name of a field.

Using the except command
var results = jLinq.from(data.fruits)
    .orderBy()
    .reverse() // should be backwards 
    .select();