Odpowiednik JavaScript do języka C # LINQ Select

136

Po tym pytaniu tutaj:

Użycie zaznaczonego wiązania w knockout z listą pól wyboru powoduje sprawdzenie wszystkich pól wyboru

Utworzyłem kilka pól wyboru za pomocą knockout, które umożliwiają wybór z tablicy. skrzypce robocze wzięte z powyższego postu:

http://jsfiddle.net/NsCXJ/

Czy istnieje prosty sposób na utworzenie tablicy samych identyfikatorów owocu?

Jestem bardziej w domu z C #, w którym robiłbym coś na wzór selectedFruits.select(fruit=>fruit.id);

Czy jest jakaś metoda / gotowa funkcja do zrobienia czegoś podobnego z javascript / jquery? A może najprostszą opcją byłoby zapętlenie listy i utworzenie drugiej tablicy? Mam zamiar wysłać tablicę z powrotem na serwer w formacie JSON, więc staram się zminimalizować wysyłane dane.

Chris Nevill
źródło

Odpowiedzi:

227

Tak, Array.map () lub $ .map () robi to samo.

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

Ponieważ array.map nie jest obsługiwana w starszych przeglądarkach, sugeruję trzymać się metody jQuery.

Jeśli z jakiegoś powodu wolisz inny, zawsze możesz dodać polyfill do obsługi starej przeglądarki.

Zawsze możesz również dodać własne metody do prototypu tablicy:

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

Rozszerzona wersja, która używa konstruktora funkcji, jeśli przekazujesz ciąg. Może coś do zabawy:

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/

Aktualizacja:

Ponieważ ta odpowiedź stała się tak popularna, dodaję podobny mój where()+ firstOrDefault(). Można ich również użyć z podejściem konstruktora funkcji opartym na ciągach (które jest najszybsze), ale tutaj jest inne podejście wykorzystujące literał obiektu jako filtr:

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

Stosowanie:

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

Oto test jsperf, aby porównać konstruktor funkcji z prędkością literału obiektu. Jeśli zdecydujesz się na użycie pierwszego, pamiętaj o poprawnym cytowaniu ciągów znaków.

Osobiście wolę używać rozwiązań opartych na literałach obiektów podczas filtrowania 1-2 właściwości i przekazywać funkcję zwrotną w celu bardziej złożonego filtrowania.

Zakończę dwoma ogólnymi wskazówkami dotyczącymi dodawania metod do prototypów obiektów natywnych:

  1. Sprawdź występowanie istniejących metod przed nadpisaniem, np .:

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

  2. Jeśli nie potrzebujesz obsługi IE8 i starszych , zdefiniuj metody przy użyciu Object.defineProperty, aby uczynić je nieliczalnymi . Jeśli ktoś użyje for..intablicy (co jest błędne w pierwszej kolejności), będzie iterował również wyliczalne właściwości. Tylko jedno ostrzeżenie.

Johan
źródło
1
@ChrisNevill Dodałem również wersję tekstową na wypadek, gdybyś był zainteresowany
Johan
@MUlferts Dobry chwyt, zaktualizowany :). Obecnie sugerowałbym stosowanie lodash do tego typu zadań. Udostępniają ten sam interfejs, co powyższy kod
Johan
Aby wesprzeć obserwacje nokautowe:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
Linus Caldwell
@LinusCaldwell Minęło dużo czasu, odkąd użyłem nokautu, ale co z czymś takim return ko.unwrap(item[property]) === filter[property]?
Johan,
Cóż, wspomniałem o nokautowaniu, ale oczywiście obejmowałoby to wszystkie właściwości, które są funkcjami bez wymaganych parametrów. Poza tym, dlaczego ktoś miałby łamać ogólny styl twojego pięknego kodu?
Linus Caldwell,
33

Wiem, że to późna odpowiedź, ale mi się przydała! Na koniec, używając $.grepfunkcji możesz emulować 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
źródło
tego właśnie chcę ... ale co jest lepsze między twoją odpowiedzią a Enumerable.From (selectedFruits) .Select (function (fruit) {return fruit.id;});
Bharat
15

Ponieważ używasz knockout, powinieneś rozważyć użycie funkcji narzędzia knockout arrayMap()i innych funkcji narzędziowych tablicy.

Oto lista funkcji narzędziowych tablic i ich równoważnych metod LINQ:

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)

Oto co możesz zrobić na swoim przykładzie:

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

Jeśli chcesz mieć interfejs podobny do LINQ w javascript, możesz użyć biblioteki, takiej jak linq.js, która oferuje ładny interfejs dla wielu metod LINQ.

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

Sposób 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);
}

również pod adresem : https://jsfiddle.net/52dpucey/

Lipiec Tech
źródło
Bardzo cenione. Właśnie wchodzę do ES6, więc to może być przydatne!
Chris Nevill,
10

Możesz też spróbować linq.js

W linq.jstwoim

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

będzie

Enumerable.From(selectedFruits).Select(function (fruit) { return fruit.id;  });
Anik Islam Abhi
źródło
4

Zbudowałem bibliotekę Linq dla TypeScript pod TsLinq.codeplex.com , której możesz użyć również dla zwykłego javascript. Ta biblioteka jest 2-3 razy szybsza niż Linq.js i zawiera testy jednostkowe dla wszystkich metod Linq. Może mógłbyś to przejrzeć.

Michael Baarz
źródło
0

Dinqyjs ma składnię podobną do linq i zapewnia wypełniacze dla funkcji takich jak map i indexOf, a także został zaprojektowany specjalnie do pracy z tablicami w JavaScript.

garryp
źródło
0

Spójrz na płynność , obsługuje prawie wszystko, co robi LINQ i opiera się na iterowalnych - więc działa z mapami, funkcjami generatora, tablicami, wszystkim iterowalnym.

kataik
źródło
-1

Odpowiadam raczej na tytuł pytania niż na pytanie oryginalne, które było bardziej szczegółowe.

Dzięki nowym funkcjom Javascript, takim jak iteratory i funkcje generatora oraz obiekty, możliwe staje się coś takiego jak LINQ for Javascript. Zwróć uwagę, że linq.js, na przykład, używa zupełnie innego podejścia, używając wyrażeń regularnych, prawdopodobnie w celu przezwyciężenia braku obsługi języka w tamtym czasie.

Mając to na uwadze, napisałem bibliotekę LINQ dla Javascript i możesz ją znaleźć na https://github.com/Siderite/LInQer . Komentarze i dyskusja na https://siderite.dev/blog/linq-in-javascript-linqer .

Z poprzednich odpowiedzi wynika, że ​​tylko Manipula wydaje się być tym, czego można by oczekiwać po porcie LINQ w JavaScript.

Siderite Zackwehdex
źródło