Javascript Equivalent to C# LINQ Select
JavascriptJqueryknockout.jsJavascript Problem Overview
Following this question here :
I've created some checkboxes using knockout that allow selection from an array. working fiddle taken from above post:
Is there a simple way of creating an array of just the fruit's ID's?
I'm more at home with C# where I would do something along the lines of selectedFruits.select(fruit=>fruit.id);
Is there some method/ready made function for doing something similar with javascript/jquery? Or would the simplest option be to loop through the list and create a second array? I intend to post the array back to the server in JSON so am trying to minimize the data sent.
Javascript Solutions
Solution 1 - Javascript
Yes, Array.map() or $.map() does the same thing.
//array.map:
var ids = this.fruits.map(function(v){
return v.Id;
});
//jQuery.map:
var ids2 = $.map(this.fruits, function (v){
return v.Id;
});
console.log(ids, ids2);
Since array.map isn't supported in older browsers, I suggest that you stick with the jQuery method.
If you prefer the other one for some reason you could always add a polyfill for old browser support.
You can always add custom methods to the array prototype as well:
Array.prototype.select = function(expr){
var arr = this;
//do custom stuff
return arr.map(expr); //or $.map(expr);
};
var ids = this.fruits.select(function(v){
return v.Id;
});
An extended version that uses the function constructor if you pass a string. Something to play around with perhaps:
Array.prototype.select = function(expr){
var arr = this;
switch(typeof expr){
case 'function':
return $.map(arr, expr);
break;
case 'string':
try{
var func = new Function(expr.split('.')[0],
'return ' + expr + ';');
return $.map(arr, func);
}catch(e){
return null;
}
break;
default:
throw new ReferenceError('expr not defined or not supported');
break;
}
};
console.log(fruits.select('x.Id'));
Update:
Since this has become such a popular answer, I'm adding similar my where()
+ firstOrDefault()
. These could also be used with the string based function constructor approach (which is the fastest), but here is another approach using an object literal as filter:
Array.prototype.where = function (filter) {
var collection = this;
switch(typeof filter) {
case 'function':
return $.grep(collection, filter);
case 'object':
for(var property in filter) {
if(!filter.hasOwnProperty(property))
continue; // ignore inherited properties
collection = $.grep(collection, function (item) {
return item[property] === filter[property];
});
}
return collection.slice(0); // copy the array
// (in case of empty object filter)
default:
throw new TypeError('func must be either a' +
'function or an object of properties and values to filter by');
}
};
Array.prototype.firstOrDefault = function(func){
return this.where(func)[0] || null;
};
Usage:
var persons = [{ name: 'foo', age: 1 }, { name: 'bar', age: 2 }];
// returns an array with one element:
var result1 = persons.where({ age: 1, name: 'foo' });
// returns the first matching item in the array, or null if no match
var result2 = persons.firstOrDefault({ age: 1, name: 'foo' });
Here is a jsperf test to compare the function constructor vs object literal speed. If you decide to use the former, keep in mind to quote strings correctly.
My personal preference is to use the object literal based solutions when filtering 1-2 properties, and pass a callback function for more complex filtering.
I'll end this with 2 general tips when adding methods to native object prototypes:
-
Check for occurrence of existing methods before overwriting e.g.:
if(!Array.prototype.where) { Array.prototype.where = ...
-
If you don't need to support IE8 and below, define the methods using Object.defineProperty to make them non-enumerable. If someone used
for..in
on an array (which is wrong in the first place) they will iterate enumerable properties as well. Just a heads up.
Solution 2 - Javascript
I know it is a late answer but it was useful to me! Just to complete, using the $.grep
function you can emulate the linq where()
.
Linq:
var maleNames = people
.Where(p => p.Sex == "M")
.Select(p => p.Name)
Javascript:
// replace where with $.grep
// select with $.map
var maleNames = $.grep(people, function (p) { return p.Sex == 'M'; })
.map(function (p) { return p.Name; });
Solution 3 - Javascript
Since you're using knockout, you should consider using the knockout utility function arrayMap()
and it's other array utility functions.
Here's a listing of array utility functions and their equivalent LINQ methods:
arrayFilter() -> Where()
arrayFirst() -> First()
arrayForEach() -> (no direct equivalent)
arrayGetDistictValues() -> Distinct()
arrayIndexOf() -> IndexOf()
arrayMap() -> Select()
arrayPushAll() -> (no direct equivalent)
arrayRemoveItem() -> (no direct equivalent)
compareArrays() -> (no direct equivalent)
So what you could do in your example is this:
var mapped = ko.utils.arrayMap(selectedFruits, function (fruit) {
return fruit.id;
});
If you want a LINQ like interface in javascript, you could use a library such as linq.js which offers a nice interface to many of the LINQ methods.
var mapped = Enumerable.from(selectedFruits)
.select("$.id") // shorthand for `x => x.id`
.toArray();
Solution 4 - Javascript
The ES6 way:
let people = [{firstName:'Alice',lastName:'Cooper'},{firstName:'Bob',age:'Dylan'}];
let names = Array.from(people, p => p.firstName);
for (let name of names) {
console.log(name);
}
also at: https://jsfiddle.net/52dpucey/
Solution 5 - Javascript
You can also try linq.js
In linq.js
your
selectedFruits.select(fruit=>fruit.id);
will be
Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id; });
Solution 6 - Javascript
I have build a Linq library for TypeScript under TsLinq.codeplex.com that you can use for plain javascript too. That library is 2-3 times faster than Linq.js and contains unit tests for all Linq methods. Maybe you could review that one.
Solution 7 - Javascript
The most similar C# Select
analogue would be a map
function.
Just use:
var ids = selectedFruits.map(fruit => fruit.id);
to select all ids from selectedFruits
array.
It doesn't require any external dependencies, just pure JavaScript. You can find map
documentation here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
Solution 8 - Javascript
Take a peek at underscore.js which provides many linq like functions. In the example you give you would use the map function.
Solution 9 - Javascript
You can try manipula
package, which implement all of C# LINQ methods and save its syntax:
https://github.com/litichevskiydv/manipula
https://www.npmjs.com/package/manipula
Your example selectedFruits.select(fruit=>fruit.id);
will be implemented with manipula as
Manipula.from(selectedFruits).select(fruit=>fruit.id);
Solution 10 - Javascript
[Dinqyjs](http://dinqyjs.com "Dinqyjs") has a linq-like syntax and provides polyfills for functions like map and indexOf, and has been designed specifically for working with arrays in Javascript.
Solution 11 - Javascript
I am answering the title of the question rather than the original question which was more specific.
With the new features of Javascript like iterators and generator functions and objects, something like LINQ for Javascript becomes possible. Note that linq.js, for example, uses a completely different approach, using regular expressions, probably to overcome the lack of support in the language at the time.
With that being said, I've written a LINQ library for Javascript and you can find it at https://github.com/Siderite/LInQer. Comments and discussion at https://siderite.dev/blog/linq-in-javascript-linqer.
From previous answers, only Manipula seems to be what one would expect from a LINQ port in Javascript.
Solution 12 - Javascript
Take a look at fluent, it supports almost everything LINQ does and based on iterables - so it works with maps, generator functions, arrays, everything iterable.
Solution 13 - Javascript
As for 2021, there is a full implementation of LINQ to object written with the latest feature of ECMAScript (javascript).
The Github repository is: https://github.com/IlanAmoyal/WebPartyLinq