Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript Equivalent to C# LINQ Select

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);

http://jsfiddle.net/NsCXJ/1/

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'));

http://jsfiddle.net/aL85j/

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:

  1. Check for occurrence of existing methods before overwriting e.g.:

    if(!Array.prototype.where) { Array.prototype.where = ...

  2. 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.


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; });

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/


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();

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;  });