jLinq is a fully extensible Javascript library that allows you to perform LINQ style queries on arrays of object.
jLinq is on CodePlex so you can contribute ideas, code or documentation!
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).
There are already sets of records you loaded onto this demo page. You can view a list of the available arrays of records.
data.users.So, as shown before, you can use the Javascript var results = jLinq.from(data.nameOfRecords)...
to start selecting records.
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.
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.
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().
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.
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.
//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.
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?
//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();
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.
//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'])
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.
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.
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
};
});
By joining records you have access to their nested properties. You can even use those properties in queries. For example...
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.
var results = jLinq.from(data.users)
.equals("permissions[1]", "write")
.select();
jLinq will also allow you to access methods that are part of the record. For example...
var results = jLinq.from([
{ id:4, getId:function() { return this.id; }},
{ id:8, getId:function() { return this.id; }}
])
.notGreater("getId()", 5)
.select();
If you join a set of records, you can access those properties just as you normally would. For example...
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.
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.
//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();
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.
//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();
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.
//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();
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...
//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!)
String comparisons are not case sensitive by default. (Most string comparisons are done using Regular Expressions).
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().
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).
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.
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.
jLinq supports for up to 25 different sorting orders. To use more criteria for your sorting just add them as another parameter.
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.
var results = jLinq.from(data.users)
.orderBy("-age") //descending
.select();
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).
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; })
};
});
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.
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 contains sample information that contains a lot of common information. It
also includes a locationId that can be joined with data.locations.
//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 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.
//data.locations record sample
{
id: 1,
city:"Abilene",
state:"Texas"
}
data.fruits is a simple string array without any properties, each with a name of
a fruit.
//data.fruits record sample
[
"apple",
"orange",
"bannana"
//... etc...
]
data.numbers is similar to the data.fruits array in that is contains
a list of numbers without any properties.
//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.
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.
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.
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.
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.
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.
//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.
//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.
//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.
var results = jLinq.from(data.fruits)
.orderBy()
.reverse() // should be backwards
.select();