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.

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.


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

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

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/


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

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




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.


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.



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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.