Javascript equivalente a C # LINQ Select


137

Seguendo questa domanda qui:

Utilizzando l'associazione verificata in knockout con un elenco di caselle di controllo controlla tutte le caselle di controllo

Ho creato alcune caselle usando knockout che consentono la selezione da un array. violino di lavoro preso dall'alto post:

http://jsfiddle.net/NsCXJ/

Esiste un modo semplice per creare una matrice di soli ID del frutto?

Sono più a mio agio con C # dove farei qualcosa del genere selectedFruits.select(fruit=>fruit.id);

C'è qualche metodo / funzione pronta per fare qualcosa di simile con javascript / jquery? O l'opzione più semplice sarebbe quella di scorrere l'elenco e creare un secondo array? Intendo inviare nuovamente l'array al server in JSON, quindi sto cercando di ridurre al minimo i dati inviati.

Risposte:


227

Sì, Array.map () o $ .map () fa la stessa cosa.

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

Dato che array.map non è supportato nei browser meno recenti, ti suggerisco di seguire il metodo jQuery.

Se preferisci l'altro per qualche motivo, puoi sempre aggiungere un polyfill per il vecchio supporto del browser.

Puoi sempre aggiungere metodi personalizzati anche al prototipo dell'array:

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

Una versione estesa che utilizza la funzione di costruzione se si passa una stringa. Qualcosa con cui giocare forse:

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/

Aggiornare:

Poiché questa è diventata una risposta così popolare, sto aggiungendo il mio where()+ simile firstOrDefault(). Questi potrebbero anche essere usati con l'approccio del costruttore di funzioni basato su stringhe (che è il più veloce), ma ecco un altro approccio che utilizza un oggetto letterale come filtro:

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

Uso:

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

Ecco un test jsperf per confrontare la funzione di costruzione della funzione con la velocità letterale dell'oggetto. Se decidi di utilizzare il primo, tieni presente di citare correttamente le stringhe.

La mia preferenza personale è quella di utilizzare le soluzioni letterali basate sull'oggetto quando si filtrano 1-2 proprietà e passare una funzione di callback per un filtro più complesso.

Concluderò con 2 suggerimenti generali quando si aggiungono metodi ai prototipi di oggetti nativi:

  1. Verificare la presenza di metodi esistenti prima di sovrascrivere, ad esempio:

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

  2. Se non è necessario supportare IE8 e versioni successive, definire i metodi utilizzando Object.defineProperty per renderli non enumerabili. Se qualcuno ha usato for..inun array (che è sbagliato in primo luogo) itererà anche le proprietà enumerabili. Solo un avviso.


1
@ChrisNevill Ho aggiunto anche una versione di stringa nel caso in cui tu sia interessato
Johan

@MUlferts Buona cattura, aggiornato :). Oggi suggerirei di usare lodash per questo tipo di compiti. Espongono la stessa interfaccia del codice sopra
Johan

Per supportare osservabili knockout:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
Linus Caldwell

@LinusCaldwell È da tanto che non uso il knockout, ma che ne dici di qualcosa del genere return ko.unwrap(item[property]) === filter[property]?
Johan,

Bene, ho citato knockout, ma ovviamente questo coprirebbe tutte le proprietà che sono funzioni senza parametri richiesti. Inoltre, perché rompere lo stile generico del tuo bellissimo codice?
Linus Caldwell,

33

So che è una risposta tardiva ma mi è stata utile! Solo per completare, usando la $.grepfunzione è possibile emulare il 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; });

questo è quello che voglio ... ma ciò che è più buono tra la tua risposta e Enumerable.From (selectedFruits) .Select (function (fruit) {return fruit.id;});
Bharat,

15

Dato che stai usando knockout, dovresti considerare di usare la funzione di knockout utility arrayMap()e le sue altre funzioni di utility array.

Ecco un elenco delle funzioni di utilità dell'array e dei loro metodi LINQ equivalenti:

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)

Quindi quello che potresti fare nel tuo esempio è questo:

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

Se si desidera un'interfaccia simile a LINQ in javascript, è possibile utilizzare una libreria come linq.js che offre un'interfaccia gradevole a molti dei metodi LINQ.

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

14

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

anche su: https://jsfiddle.net/52dpucey/


Molto apprezzato. Sto solo entrando in ES6, quindi potrebbe essere utile!
Chris Nevill,

10

Puoi anche provare linq.js

Nel linq.jstuo

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

sarà

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

4

Ho creato una libreria Linq per TypeScript sotto TsLinq.codeplex.com che puoi usare anche per javascript. Quella libreria è 2-3 volte più veloce di Linq.js e contiene unit test per tutti i metodi Linq. Forse potresti rivederlo.




0

Dinqyjs ha una sintassi simile a linq e fornisce polyfill per funzioni come map e indexOf, ed è stato progettato specificamente per lavorare con array in Javascript.


0

Dai un'occhiata fluida , supporta quasi tutto ciò che LINQ fa e basato su iterabili - quindi funziona con mappe, funzioni del generatore, array, tutto iterabile.



-1

Sto rispondendo al titolo della domanda piuttosto che alla domanda originale che era più specifica.

Con le nuove funzionalità di Javascript come iteratori e funzioni e oggetti del generatore, diventa possibile qualcosa come LINQ per Javascript. Si noti che linq.js, ad esempio, utilizza un approccio completamente diverso, usando espressioni regolari, probabilmente per ovviare alla mancanza di supporto nella lingua in quel momento.

Detto questo, ho scritto una libreria LINQ per Javascript e puoi trovarla su https://github.com/Siderite/LInQer . Commenti e discussioni su https://siderite.dev/blog/linq-in-javascript-linqer .

Dalle risposte precedenti, solo Manipula sembra essere quello che ci si aspetterebbe da una porta LINQ in Javascript.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.