Javascript еквівалентний C # LINQ Select


136

Після цього питання тут:

Використовуючи відмічену прив'язку в нокауті зі списком прапорців, перевіряйте всі прапорці

Я створив деякі прапорці за допомогою нокауту, які дозволяють виділити з масиву. робоча скрипка, взята з верхньої посади:

http://jsfiddle.net/NsCXJ/

Чи є простий спосіб створити масив лише ідентифікаторів фруктів?

Я більше вдома з C #, де я би щось робив за принципом selectedFruits.select(fruit=>fruit.id);

Чи є якась методика / готова функція для того, щоб робити щось подібне з javascript / jquery? Або найпростішим варіантом буде прокручування списку та створення другого масиву? Я маю намір опублікувати масив назад на сервері в JSON, тому намагаюся мінімізувати надіслані дані.

Відповіді:


227

Так, Array.map () або $ .map () робить те саме.

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

Оскільки array.map не підтримується у старих браузерах, я пропоную вам дотримуватися методу jQuery.

Якщо ви віддаєте перевагу іншому з якихось причин, ви завжди можете додати поліфайл для старої підтримки браузера.

Ви завжди можете додавати власні методи до прототипу масиву:

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

Розширена версія, що використовує конструктор функцій, якщо ви передаєте рядок. З чим, можливо, пограти:

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/

Оновлення:

Оскільки це стало такою популярною відповіддю, я додаю подібний мій where()+ firstOrDefault(). Вони також можуть бути використані з підходом конструктора функцій на основі рядків (який найшвидший), але ось інший підхід, що використовує об'єктний буквал як фільтр:

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

Використання:

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

Ось тест jsperf для порівняння конструктора функцій з швидкістю прямої об’єкта. Якщо ви вирішили використовувати перший, майте на увазі правильно цитувати рядки.

Моє особисте перевагу - використовувати об'єктні літеральні рішення при фільтруванні 1-2 властивостей і передавати функцію зворотного виклику для більш складної фільтрації.

Я закінчу це двома загальними порадами щодо додавання методів до прототипів нативного об’єкта:

  1. Перевірте наявність існуючих методів перед перезаписом, наприклад:

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

  2. Якщо вам не потрібно підтримувати IE8 і нижче, визначте методи, використовуючи Object.defineProperty, щоб зробити їх нечисленними. Якщо хтось використовував for..inмасив (що в першу чергу неправильно), він також повторить численні властивості. Просто голови вгору.


1
@ChrisNevill Я додав також рядкову версію, якщо вас цікавить
Йоган

@MUlferts Хороший улов, оновлено :). Сьогодні я б запропонував використовувати лодаш для подібних завдань. Вони розкривають той самий інтерфейс, що і код вище
Йоган

Для підтримки спостереження за нокаутом:return typeof item[property] === 'function' ? item[property]() === filter[property] : item[property] === filter[property];
Лінус Колдвелл

@LinusCaldwell Давно минув час, коли я використовував нокаут, а як щодо чогось return ko.unwrap(item[property]) === filter[property]?
Йоган

Ну, я згадав про нокаут, але, звичайно, це охоплюватиме всі властивості, які є функціями без необхідних параметрів. Крім того, чому б ви порушили загальний стиль вашого прекрасного коду?
Лінус Колдвелл

33

Я знаю, що це пізня відповідь, але мені це було корисно! Просто для завершення, використовуючи $.grepфункцію, ви можете імітувати linkq 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; });

це те, що я хочу .. але що є кращим між вашою відповіддю та "Численним". Від (вибрані фрукти). Виберіть (функція (фрукти) {повертайте фрукт.id;});
Бхарат

15

Оскільки ви користуєтесь нокаутом, вам слід розглянути можливість використання утиліти вибивання arrayMap()та інших функцій утиліти масиву.

Ось перелік функцій утиліти масиву та їх еквівалентних методів 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)

Отже, що ви можете зробити у своєму прикладі:

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

Якщо ви хочете інтерфейс, подібний LINQ, у JavaScript, ви можете використовувати бібліотеку, таку як linq.js, яка пропонує приємний інтерфейс для багатьох методів LINQ.

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



4

У мене є збірка бібліотеки Linq для TypeScript під TsLinq.codeplex.com, яку ви також можете використовувати для звичайного javascript. Ця бібліотека в 2-3 рази швидша, ніж Linq.js, і містить одиничні тести для всіх методів Linq. Можливо, ви могли це переглянути.


2

Погляньте на underscore.js, який надає багато функцій, подібних linq. У наведеному прикладі ви б використали функцію карти.


1
Якщо хтось хоче знати, як вони порівнюють, я зробив допис у блозі, де пояснюється різниця між більш ніж 15 найпопулярнішими функціями LINQ / underscore.js: vladopandzic.com/javascript/comparing-underscore-js-with-linq
Владо Панджич


0

Dinqyjs має синтаксис, що нагадує linq, і забезпечує полізаповнення для таких функцій, як map та indexOf, і був розроблений спеціально для роботи з масивами в Javascript.


0

Погляньте на вільне слово, він підтримує майже все, що робить LINQ, і базується на ітерабелях - тому він працює з картами, генераторними функціями, масивами, усім ітерабельним.


0

Найбільш схожий Selectаналог C # буде mapфункцією. Просто використовуйте:

var ids = selectedFruits.map(fruit => fruit.id);

щоб вибрати всі ідентифікатори з selectedFruitsмасиву.

Для цього не потрібні зовнішні залежності, просто чистий JavaScript. mapДокументацію можна знайти тут: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map


-1

Я відповідаю на назву питання, а не на первісне питання, яке було більш конкретним.

З новими можливостями Javascript, такими як ітератори та функції генератора та об'єкти, стає можливим щось на зразок LINQ для Javascript. Зауважте, що, наприклад, linq.js використовує зовсім інший підхід, використовуючи регулярні вирази, ймовірно, щоб подолати відсутність підтримки мови в той час.

З урахуванням цього, я написав бібліотеку LINQ для Javascript, і ви можете знайти її за адресою https://github.com/Siderite/LInQer . Коментарі та обговорення на https://siderite.dev/blog/linq-in-javascript-linqer .

З попередніх відповідей, здається, що Manipula є тим, чого можна було б очікувати від порту LINQ у Javascript.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.