web-dev-qa-db-ja.com

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

LINQ SingleOrDefault メソッドに似たJavaScript拡張を作成することを考えています。配列にない場合はnullを返します:

var item = vegetable.singleOrDefault( xvegetable => {
    return xvegetable.id == id});

私の質問は、カスタムインターフェイスを作成せずにこれを達成する別の方法があるかどうかです。

28
howardlo

常に 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は、一致するものが1つだけであるかどうかを確認します。私の場合(およびコード内)、別の一致を確認せずに最初の一致を返します。

ところで、SingleOrDefaultを実現したい場合は、これを変更する必要があります。

var item = collection.length < 1 ? null : collection[0];

if(collection.length > 1)
   throw "Not single result....";

return collection.length === 0 ? null : collection[0];
30
Amir Popovich

配列内の1つのアイテムを検索する場合は、 ES6 find method を使用することをお勧めします。

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]と書く必要がないため、読みやすくなります。

ところで、C#のSingleOrDefaultは、複数の要素が見つかった場合に例外をスローします。ここでは、FirstOrDefault拡張機能を作成しようとしています。

22

再利用が不要な場合は、単純な reduce 関数を適用してsingleOrDefaultを実装できます。

この場合、reducer関数は、配列が空でない場合にのみ計算されます。長さが1より大きい場合にスローされます。そうでない場合は、唯一の要素を返します。配列が空の場合、reduce関数のデフォルト値パラメーターが返されます。この場合はnullです。

例えば:

[].reduce(function(acc, cur, idx, src) {
  if (src.length > 1) {
    throw 'More than one found';
  }
  return src[0];
}, null);
1
Philip Bijker

現在、TypeScriptで厳密に型指定されたクエリ可能なコレクションを提供するライブラリがあります。

ライブラリはts-generic-collectionsと呼ばれます。

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's 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(); 
0
John