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.

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.


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

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

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/


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

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.




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.


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.



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

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.