179 votes

Équivalent JavaScript à C# LINQ Select

Suite à cette question ici :

Utiliser la liaison checked dans knockout avec une liste de cases à cocher coche toutes les cases à cocher

J'ai créé quelques cases à cocher en utilisant knockout qui permettent une sélection à partir d'un tableau. Fiddle fonctionnel pris du post ci-dessus :

http://jsfiddle.net/NsCXJ/

Y a-t-il un moyen simple de créer un tableau uniquement avec les identifiants des fruits ?

Je suis plus habitué à C# où je ferais quelque chose comme selectedFruits.select(fruit=>fruit.id);

Y a-t-il une méthode/fonction prête à l'emploi pour faire quelque chose de similaire avec javascript/jquery ? Ou la meilleure option serait-elle de parcourir la liste et de créer un second tableau ? Je compte renvoyer le tableau au serveur en JSON, donc j'essaie de minimiser les données envoyées.

283voto

Johan Points 10310

Oui, Array.map() ou $.map() font 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/

Puisque array.map n'est pas supporté dans les anciens navigateurs, je suggère de rester avec la méthode jQuery.

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

Vous pouvez toujours ajouter des méthodes personnalisées au prototype d'array également:

Array.prototype.select = function(expr){
    var arr = this;
    //faire des choses personnalisées
    return arr.map(expr); //ou $.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 à expérimenter 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 non défini ou non supporté');
            break;
    }

};

console.log(fruits.select('x.Id'));

http://jsfiddle.net/aL85j/

Mise à jour:

Puisque cela est devenu une réponse si populaire, j'ajoute ma méthode where() + firstOrDefault() similaires. Celles-ci pourraient également être utilisées avec l'approche du constructeur de fonction basée sur une chaîne (qui est la plus rapide), mais voici une autre approche utilisant un objet littéral 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; // ignorer les propriétés héritées

              collection = $.grep(collection, function (item) {
                  return item[property] === filter[property];
              });
            }
            return collection.slice(0); // copie du tableau 
                                      // (en cas de filtre vide)

        default: 
            throw new TypeError('func doit être soit une ' +
                'fonction, soit un objet de propriétés et de valeurs à filtrer par'); 
    }
};

Array.prototype.firstOrDefault = function(func){
    return this.where(func)[0] || null;
};

Utilisation:

var persons = [{ name: 'foo', age: 1 }, { name: 'bar', age: 2 }];

// retourne un tableau avec un élément:
var result1 = persons.where({ age: 1, name: 'foo' });

// retourne le premier élément correspondant dans le tableau, ou null s'il n'y a pas de correspondance
var result2 = persons.firstOrDefault({ age: 1, name: 'foo' }); 

Voici un test jsperf pour comparer la vitesse du constructeur de fonction par rapport à l'objet littéral. Si vous décidez d'utiliser le premier, rappelez-vous de citer correctement les chaînes.

Ma préférence personnelle est d'utiliser les solutions basées sur des objets littéraux lors du filtrage de 1 à 2 propriétés, et de passer une fonction de rappel pour un filtrage plus complexe.

Je terminerai avec 2 conseils généraux lors de l'ajout de méthodes aux prototypes d'objets natifs:

  1. Vérifiez l'existence de méthodes existantes avant de les écraser par exemple:

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

  2. Si vous n'avez pas besoin de prendre en charge IE8 et versions antérieures, définissez les méthodes en utilisant Object.defineProperty pour les rendre non énumérables. Si quelqu'un utilisait for..in sur un tableau (ce qui est incorrect en premier lieu), il itérera également sur les propriétés énumérables. Juste une mise en garde.

34voto

Stefano Altieri Points 2522

Je sais que c'est une réponse tardive mais elle m'a été utile! Juste pour compléter, en utilisant la fonction $.grep vous pouvez émuler le where() de Linq.

Linq:

var maleNames = people
.Where(p => p.Sex == "M")
.Select(p => p.Name)

Javascript:

// remplacer where avec $.grep
//         select avec $.map
var maleNames = $.grep(people, function (p) { return p.Sex == 'M'; })
            .map(function (p) { return p.Name; });

20voto

yasvintus Points 61

La méthode 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 disponible sur : https://jsfiddle.net/52dpucey/

18voto

Jeff Mercado Points 42075

Depuis que vous utilisez knockout, vous devriez envisager d'utiliser la fonction utilitaire knockout arrayMap() et ses autres fonctions utilitaires de tableau.

Voici une liste des fonctions utilitaires de tableau et de leurs méthodes LINQ équivalentes :

arrayFilter() -> Where()
arrayFirst() -> First()
arrayForEach() -> (pas d'équivalent direct)
arrayGetDistictValues() -> Distinct()
arrayIndexOf() -> IndexOf()
arrayMap() -> Select()
arrayPushAll() -> (pas d'équivalent direct)
arrayRemoveItem() -> (pas d'équivalent direct)
compareArrays() -> (pas d'équivalent direct)

Donc ce que vous pourriez faire dans votre exemple est le suivant :

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

Si vous souhaitez une interface de style LINQ en javascript, vous pourriez utiliser une bibliothèque telle que linq.js qui offre une belle interface pour de nombreuses des méthodes LINQ.

var mapped = Enumerable.from(selectedFruits)
    .select("$.id") // raccourci pour `x => x.id`
    .toArray();

11voto

Anik Islam Abhi Points 22148

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X