Эквивалент LINQ SingleOrDefault()
В Typescript я часто использую этот шаблон:
class Vegetable {
constructor(public id: number, public name: string) {
}
}
var vegetable_array = new Array<Vegetable>();
vegetable_array.push(new Vegetable(1, "Carrot"));
vegetable_array.push(new Vegetable(2, "Bean"));
vegetable_array.push(new Vegetable(3, "Peas"));
var id = 1;
var collection = vegetable_array.filter( xvegetable => {
return xvegetable.id == id;
});
var item = collection.length < 1 ? null : collection[0];
console.info( item.name );
Я думаю о создании расширения JavaScript, похожий на LINQ SingleOrDefault
метод, при котором он возвращает null
, если он не в массиве:
var item = vegetable.singleOrDefault( xvegetable => {
return xvegetable.id == id});
У меня вопрос, есть ли другой способ добиться этого без создания пользовательского интерфейса?
Ответы
Ответ 1
Вы всегда можете использовать Array.prototype.filter следующим образом:
var arr = [1,2,3];
var notFoundItem = arr.filter(id => id === 4)[0]; // will return undefined
var foundItem = arr.filter(id => id === 3)[0]; // will return 3
Изменить
Мой ответ относится к FirstOrDefault
, а не к SingleOrDefault
.
SingleOrDefault
проверяет, есть ли только одно совпадение, и в моем случае (и в вашем коде) вы возвращаете первое совпадение, не проверяя наличие другого соответствия.
Кстати, если вы хотите достичь SingleOrDefault
, вам нужно будет изменить это:
var item = collection.length < 1 ? null : collection[0];
в
if(collection.length > 1)
throw "Not single result....";
return collection.length === 0 ? null : collection[0];
Ответ 2
Если вы хотите найти один элемент в массиве, я предлагаю вам использовать метод поиска ES6, например, так:
const inventory = [
{ name: 'apples', quantity: 2 },
{ name: 'bananas', quantity: 0 },
{ name: 'cherries', quantity: 5 }
];
const result = inventory.find(fruit => fruit.name === 'cherries');
console.log(result) // { name: 'cherries', quantity: 5 }
Это быстрее и проще для чтения, потому что вам не нужно писать collection[0]
.
Кстати, С# SingleOrDefault
выдает исключение, если найдено более одного элемента. Здесь вы пытаетесь создать расширение FirstOrDefault
.
Ответ 3
Теперь есть библиотека, которая предоставляет строго типизированные, запрашиваемые коллекции в машинописи.
Библиотека называется ts-generic-collection.
Исходный код на GitHub:
https://github.com/VeritasSoftware/ts-generic-collections
С помощью этой библиотеки вы можете писать запросы, как показано ниже:
let owners = new List<Owner>();
let owner = new Owner();
owner.id = 1;
owner.name = "John Doe";
owners.add(owner);
owner = new Owner();
owner.id = 2;
owner.name = "Jane Doe";
owners.add(owner);
let pets = new List<Pet>();
let pet = new Pet();
pet.ownerId = 2;
pet.name = "Sam";
pet.sex = Sex.M;
pets.add(pet);
pet = new Pet();
pet.ownerId = 1;
pet.name = "Jenny";
pet.sex = Sex.F;
pets.add(pet);
//query to get owners by their pet gender/sex
let ownersByPetSex = owners.join(pets, owner => owner.id, pet => pet.ownerId, (x, y) => new OwnerPet(x,y))
.groupBy(x => [x.pet.sex])
.select(x => new OwnersByPetSex(x.groups[0], x.list.select(x => x.owner)));
expect(ownersByPetSex.toArray().length === 2).toBeTruthy();
expect(ownersByPetSex.toArray()[0].sex == Sex.F).toBeTruthy();
expect(ownersByPetSex.toArray()[0].owners.length === 1).toBeTruthy();
expect(ownersByPetSex.toArray()[0].owners.toArray()[0].name == "John Doe").toBeTruthy();
expect(ownersByPetSex.toArray()[1].sex == Sex.M).toBeTruthy();
expect(ownersByPetSex.toArray()[1].owners.length == 1).toBeTruthy();
expect(ownersByPetSex.toArray()[1].owners.toArray()[0].name == "Jane Doe").toBeTruthy();
Ответ 4
Если повторное использование не является необходимым, вы могли бы реализовать singleOrDefault
путем применения простого снижения функции:
В этом случае функция редуктора рассчитывается только тогда, когда массив не пустой. Выдает, когда длина больше 1, в противном случае возвращается единственный элемент. Когда массив пуст, возвращается параметр значения по умолчанию функции Reduce: в этом случае null
.
Например:
[].reduce(function(acc, cur, idx, src) {
if (src.length > 1) {
throw 'More than one found';
}
return src[0];
}, null);