Javascript équivalent à C # LINQ Select


137

Suite à cette question ici:

L'utilisation de la liaison cochée dans KO avec une liste de cases à cocher coche toutes les cases

J'ai créé des cases à cocher en utilisant knockout qui permettent la sélection à partir d'un tableau. violon de travail pris du poste 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 du genre selectedFruits.select(fruit=>fruit.id);

Existe-t-il une méthode / fonction prête à l'emploi pour faire quelque chose de similaire avec javascript / jquery? Ou l'option la plus simple serait-elle de parcourir la liste et de créer un deuxième tableau? J'ai l'intention de publier le tableau sur le serveur en JSON, j'essaie donc de minimiser les données envoyées.

Réponses:


227

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/

Puisque array.map n'est pas pris en charge dans les navigateurs plus anciens, 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 le support des anciens navigateurs.

Vous pouvez également 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 pour 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/

Mettre à jour:

Puisque cela est devenu une réponse si populaire, j'ajoute mon where()+ similaire firstOrDefault(). Ceux-ci pourraient également être utilisés 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 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 fonction à la vitesse littérale de l'objet. Si vous décidez d'utiliser l'ancien, n'oubliez pas de citer correctement les chaînes.

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

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

  1. Vérifiez l'occurrence des méthodes existantes avant d'écraser, 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 en utilisant Object.defineProperty pour les rendre non énumérables. Si quelqu'un a utilisé for..insur un tableau (ce qui est faux en premier lieu), il itérera également des propriétés énumérables. Juste un avertissement.


1
@ChrisNevill J'ai également ajouté une version string au cas où vous seriez intéressé
Johan

@MUlferts Bonne prise, mise à jour :). De nos jours, je suggérerais d'utiliser lodash pour ce genre de tâches. Ils exposent la même interface que le code ci
Johan

Pour soutenir les observables à élimination directe:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
Linus Caldwell

@LinusCaldwell Cela fait longtemps que je n'ai pas utilisé KO, mais qu'en est-il de quelque chose comme ça return ko.unwrap(item[property]) === filter[property]?
Johan

Eh bien, j'ai mentionné knockout, mais bien sûr, cela couvrirait toutes les propriétés qui sont des fonctions sans paramètres requis. D'ailleurs, pourquoi briserait-on le style générique de votre beau code?
Linus Caldwell

33

Je sais que c'est une réponse tardive mais cela m'a été utile! Juste pour terminer, en utilisant la $.grepfonction, vous pouvez émuler le 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; });

c'est ce que je veux..mais ce qui est plus bon entre votre réponse et Enumerable.From (selectedFruits) .Select (function (fruit) {return fruit.id;});
Bharat

15

Puisque 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 de l'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)

Donc, ce que vous pourriez faire dans votre exemple est ceci:

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

Si vous voulez une interface de type LINQ en javascript, vous pouvez utiliser une bibliothèque telle que linq.js qui offre une interface agréable à de nombreuses méthodes LINQ.

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

14

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 sur: https://jsfiddle.net/52dpucey/


Très appréciée. Je viens juste d'entrer dans ES6 donc cela pourrait être pratique!
Chris Nevill

10

Vous pouvez également essayer linq.js

Dans linq.jsvotre

selectedFruits.select(fruit=>fruit.id);

sera

Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id;  });

4

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




0

Dinqyjs a une syntaxe de type linq et fournit des polyfills pour des fonctions comme map et indexOf, et a été spécialement conçu pour travailler avec des tableaux en Javascript.


0

Jetez un œil à fluent , il prend en charge presque tout ce que fait LINQ et basé sur des itérables - il fonctionne donc avec des cartes, des fonctions de générateur, des tableaux, tout ce qui est itérable.



-1

Je réponds au titre de la question plutôt qu'à la question initiale qui était plus précise.

Avec les nouvelles fonctionnalités de Javascript comme les itérateurs et les fonctions et objets de générateur, quelque chose comme LINQ pour Javascript devient possible. Notez que linq.js, par exemple, utilise une approche complètement différente, utilisant des expressions régulières, probablement pour pallier le manque de support dans le langage à l'époque.

Cela étant dit, j'ai écrit une bibliothèque LINQ pour Javascript et vous pouvez la trouver sur https://github.com/Siderite/LInQer . Commentaires et discussion sur https://siderite.dev/blog/linq-in-javascript-linqer .

D'après les réponses précédentes, seul Manipula semble être ce que l'on attend d'un port LINQ en Javascript.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.