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.

Chris Nevill
la source

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.

Johan
la source
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; });
Stefano Altieri
la source
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();
Jeff Mercado
la source
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/

Juillet.Tech
la source
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;  });
Anik Islam Abhi
la source
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à.

Michael Baarz
la source
2

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

Lapin bourru
la source
1
Si quelqu'un veut savoir comment il se compare, j'ai publié un article de blog qui explique la différence entre plus de 15 fonctions LINQ / underscore.js les plus populaires: vladopandzic.com/javascript/comparing-underscore-js-with-linq
Vlado Pandžić
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.

Garryp
la source
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.

kataik
la source
-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.

Sidérite Zackwehdex
la source