Javascript Entspricht C # LINQ Select

136

Folgende Frage hier:

Wenn Sie die aktivierte Bindung im Knockout mit einer Liste von Kontrollkästchen verwenden, werden alle Kontrollkästchen aktiviert

Ich habe einige Kontrollkästchen mit Knockout erstellt, die die Auswahl aus einem Array ermöglichen. Arbeitsgeige aus dem obigen Beitrag:

http://jsfiddle.net/NsCXJ/

Gibt es eine einfache Möglichkeit, nur die IDs der Früchte zu erstellen?

Ich bin eher zu Hause bei C #, wo ich etwas in der Art von tun würde selectedFruits.select(fruit=>fruit.id);

Gibt es eine Methode / fertige Funktion, um etwas Ähnliches mit Javascript / jquery zu tun? Oder wäre die einfachste Möglichkeit, die Liste zu durchlaufen und ein zweites Array zu erstellen? Ich beabsichtige, das Array in JSON auf den Server zurückzusenden, um die gesendeten Daten zu minimieren.

Chris Nevill
quelle

Antworten:

227

Ja, Array.map () oder $ .map () machen dasselbe.

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

Da array.map in älteren Browsern nicht unterstützt wird, sollten Sie sich an die jQuery-Methode halten.

Wenn Sie aus irgendeinem Grund die andere bevorzugen, können Sie jederzeit eine Polyfüllung für die Unterstützung alter Browser hinzufügen.

Sie können dem Array-Prototyp auch jederzeit benutzerdefinierte Methoden hinzufügen:

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

Eine erweiterte Version, die den Funktionskonstruktor verwendet, wenn Sie eine Zeichenfolge übergeben. Vielleicht etwas zum Herumspielen:

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/

Aktualisieren:

Da dies eine so beliebte Antwort geworden ist, füge ich ähnlich mein where()+ hinzu firstOrDefault(). Diese können auch mit dem String-basierten Funktionskonstruktor-Ansatz (der am schnellsten ist) verwendet werden. Hier ist jedoch ein anderer Ansatz, bei dem ein Objektliteral als Filter verwendet wird:

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

Verwendung:

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

Hier ist ein jsperf-Test , um den Funktionskonstruktor mit der Objektliteralgeschwindigkeit zu vergleichen. Wenn Sie sich für Ersteres entscheiden, denken Sie daran, Zeichenfolgen korrekt zu zitieren.

Meine persönliche Präferenz ist es, beim Filtern von 1-2 Eigenschaften die auf Objektliteral basierenden Lösungen zu verwenden und eine Rückruffunktion für eine komplexere Filterung zu übergeben.

Ich werde dies mit zwei allgemeinen Tipps beenden, wenn Sie Methoden zu nativen Objektprototypen hinzufügen:

  1. Überprüfen Sie vor dem Überschreiben, ob vorhandene Methoden vorhanden sind, z.

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

  2. Wenn Sie IE8 und niedriger nicht unterstützen müssen, definieren Sie die Methoden mit Object.defineProperty , um sie nicht aufzählbar zu machen. Wenn jemand for..inein Array verwendet (was an erster Stelle falsch ist), iteriert er auch aufzählbare Eigenschaften. Nur ein Kopf hoch.

Johan
quelle
1
@ ChrisNevill Ich habe auch eine String-Version hinzugefügt, falls Sie interessiert sind
Johan
@ MUlferts Guter Fang, aktualisiert :). Heutzutage würde ich vorschlagen, lodash für diese Art von Aufgaben zu verwenden. Sie legen die gleiche Schnittstelle wie der obige Code offen
Johan
Um Knockout Observables zu unterstützen:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
Linus Caldwell
@LinusCaldwell Es ist lange her, dass ich Knockout verwendet habe, aber was ist mit so etwas return ko.unwrap(item[property]) === filter[property]?
Johan
Nun, ich habe Knockout erwähnt, aber dies würde natürlich alle Eigenschaften abdecken, die Funktionen ohne erforderliche Parameter sind. Außerdem, warum sollte man den generischen Stil Ihres schönen Codes brechen?
Linus Caldwell
33

Ich weiß, dass es eine späte Antwort ist, aber es war nützlich für mich! Zum Abschluss können Sie mit der $.grepFunktion die Linq emulieren 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
quelle
das ist was ich will ... aber was ist besser zwischen deiner Antwort und Enumerable.From (selectedFruits) .Select (Funktion (Frucht) {returnruit.id;});
Bharat
15

Da Sie Knockout verwenden, sollten Sie die Knockout-Dienstprogrammfunktion arrayMap()und andere Array-Dienstprogrammfunktionen in Betracht ziehen .

Hier ist eine Liste der Array-Dienstprogrammfunktionen und ihrer entsprechenden LINQ-Methoden:

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)

In Ihrem Beispiel könnten Sie also Folgendes tun:

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

Wenn Sie eine LINQ-ähnliche Schnittstelle in Javascript möchten, können Sie eine Bibliothek wie linq.js verwenden, die eine schöne Schnittstelle zu vielen der LINQ-Methoden bietet.

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

Der ES6-Weg:

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

auch unter: https://jsfiddle.net/52dpucey/

July.Tech
quelle
Sehr geschätzt. Ich steige gerade in ES6 ein, also könnte dies praktisch sein!
Chris Nevill
10

Sie können es auch versuchen linq.js

In linq.jsdeinem

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

wird sein

Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id;  });
Anik Islam Abhi
quelle
4

Ich habe unter TsLinq.codeplex.com eine Linq-Bibliothek für TypeScript erstellt , die Sie auch für einfaches Javascript verwenden können. Diese Bibliothek ist 2-3 mal schneller als Linq.js und enthält Unit-Tests für alle Linq-Methoden. Vielleicht könnten Sie das überprüfen.

Michael Baarz
quelle
2

Werfen Sie einen Blick auf underscore.js , das viele linq-ähnliche Funktionen bietet. In dem Beispiel, das Sie geben, würden Sie die Kartenfunktion verwenden.

Gruff Bunny
quelle
1
Wenn jemand wissen möchte, wie er vergleicht, habe ich einen Blog-Beitrag verfasst, der den Unterschied zwischen mehr als 15 der beliebtesten Funktionen von LINQ / underscore.js erklärt: vladopandzic.com/javascript/comparing-underscore-js-with-linq
Vlado Pandžić
0

Dinqyjs hat eine linq-ähnliche Syntax und bietet Polyfills für Funktionen wie map und indexOf. Es wurde speziell für die Arbeit mit Arrays in Javascript entwickelt.

garryp
quelle
0

Schauen Sie sich fließend an , es unterstützt fast alles, was LINQ tut und basiert auf iterablen Elementen - es funktioniert also mit Karten, Generatorfunktionen, Arrays und allem, was iterierbar ist.

kataik
quelle
-1

Ich beantworte eher den Titel der Frage als die ursprünglichere Frage, die spezifischer war.

Mit den neuen Funktionen von Javascript wie Iteratoren und Generatorfunktionen und -objekten wird so etwas wie LINQ für Javascript möglich. Beachten Sie, dass linq.js beispielsweise einen völlig anderen Ansatz verwendet und reguläre Ausdrücke verwendet, um wahrscheinlich den Mangel an Unterstützung in der Sprache zu überwinden.

Nachdem dies gesagt wurde, habe ich eine LINQ-Bibliothek für Javascript geschrieben, die Sie unter https://github.com/Siderite/LInQer finden . Kommentare und Diskussion unter https://siderite.dev/blog/linq-in-javascript-linqer .

Von früheren Antworten scheint nur Manipula das zu sein, was man von einem LINQ-Port in Javascript erwarten würde.

Siderit Zackwehdex
quelle