web-dev-qa-db-fra.com

JavaScript équivalent à C # LINQ Select

Suite à cette question ici:

L'utilisation de la liaison vérifiée dans Knockout avec une liste de cases à cocher vérifie toutes les cases à cocher

J'ai créé des cases à cocher en utilisant knockout qui permettent la sélection dans un tableau. violon de travail pris par le post ci-dessus:

http://jsfiddle.net/NsCXJ/

Existe-t-il un moyen simple de créer un tableau contenant uniquement les identifiants du fruit?

Je suis plus à l'aise avec C # où je ferais quelque chose dans le sens de selectedFruits.select(fruit=>fruit.id);

Existe-t-il une méthode/fonction prête à l'emploi permettant de faire quelque chose de similaire avec javascript/jquery? Ou l’option la plus simple serait-elle de parcourir la liste en boucle et de créer un second tableau? J'ai l'intention de publier le tableau sur le serveur au format JSON afin d'essayer de minimiser les données envoyées.

121
Chris Nevill

Oui, Array.map () ou $. Map () fait la même chose.

//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/

Comme array.map n'est pas supporté par les anciens navigateurs, je vous suggère de vous en tenir à la méthode jQuery.

Si vous préférez l'autre pour une raison quelconque, vous pouvez toujours ajouter un polyfill pour la prise en charge des anciens navigateurs.

Vous pouvez toujours ajouter des méthodes personnalisées au prototype de tableau:

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

Une version étendue qui utilise le constructeur de fonction si vous passez une chaîne. Quelque chose à jouer avec peut-être:

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/

Mise à jour:

Depuis que cela est devenu une réponse si populaire, j'ajoute mon similaire where() + firstOrDefault(). Celles-ci pourraient également être utilisées avec l'approche du constructeur de fonction basée sur les chaînes (qui est la plus rapide), mais voici une autre approche utilisant un littéral d'objet comme filtre:

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

Voici un test jsperf pour comparer le constructeur de la fonction à la vitesse littérale d'objet. Si vous décidez d'utiliser l'ancien, n'oubliez pas de citer les chaînes correctement.

Ma préférence personnelle est d’utiliser les solutions basées sur le littéral d’objet lors du filtrage de 1 à 2 propriétés et de transmettre une fonction de rappel pour un filtrage plus complexe.

Je terminerai par deux astuces générales lors de l’ajout de méthodes à des prototypes d’objets natifs:

  1. Vérifiez l’apparition de méthodes existantes avant de remplacer, par exemple:

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

  2. Si vous n'avez pas besoin de prendre en charge IE8 et les versions antérieures, définissez les méthodes à l'aide de Object.defineProperty pour les rendre non énumérables. Si quelqu'un a utilisé for..in sur un tableau (ce qui est faux en premier lieu), il va également itérer les propriétés énumérables. Juste un heads-up.

199
Johan

Je sais que c'est une réponse tardive mais elle m'a été utile! Pour terminer, à l’aide de la fonction $.grep, vous pouvez émuler 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; });
32
Stefano Altieri

Puisque vous utilisez knockout, vous devriez envisager d’utiliser la fonction d’utilitaire knockout arrayMap() et ses autres fonctions d’utilitaire de tableau.

Voici une liste des fonctions d’utilitaire de tableau et de leurs méthodes LINQ équivalentes:

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)

Voici ce que vous pourriez faire dans votre exemple:

var mapped = ko.utils.arrayMap(selectedFruits, function (fruit) {
    return fruit.id;
});

Si vous voulez une interface semblable à LINQ en javascript, vous pouvez utiliser une bibliothèque telle que linq.js , qui offre une interface intéressante pour de nombreuses méthodes LINQ.

var mapped = Enumerable.From(selectedFruits)
    .Select("$.id") // 1 of 3 different ways to specify a selector function
    .ToArray();
13
Jeff Mercado

La manière ES6:

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

également à: https://jsfiddle.net/52dpucey/

10
July.Tech

Vous pouvez également essayer linq.js

Dans linq.js votre

_selectedFruits.select(fruit=>fruit.id);
_

sera

_Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id;  });
_
9
Anik Islam Abhi

J'ai construit une bibliothèque Linq pour TypeScript sous TsLinq.codeplex.com que vous pouvez également utiliser pour du javascript simple. Cette bibliothèque est 2 à 3 fois plus rapide que Linq.js et contient des tests unitaires pour toutes les méthodes Linq. Peut-être que vous pourriez revoir celui-ci.

3
Michael Baarz

Jetez un coup d'œil à nderscore.js qui fournit de nombreuses fonctions analogues à linq. Dans l'exemple que vous donnez, vous utiliseriez la fonction map.

2
Gruff Bunny

Vous pouvez essayer le package manipula, qui implémente toutes les méthodes C # LINQ et enregistre sa syntaxe: https://github.com/litichevskiydv/manipula

https://www.npmjs.com/package/manipula

Votre exemple selectedFruits.select(fruit=>fruit.id); sera implémenté avec manipula comme

Manipula.from(selectedFruits).select(fruit=>fruit.id);
1
razon

Dinqyjs a une syntaxe semblable à celle de linq et fournit des polyfill pour des fonctions telles que map et indexOf, et a été spécialement conçu pour travailler avec des tableaux en Javascript.

0
garryp