LINQ SingleOrDefault()等效

时间:2015-10-11 13:44:32

标签: javascript linq typescript typescript1.5

在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 Material Design guidelines方法的JavaScript扩展,如果它不在数组中,则返回null

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

我的问题是,如果没有创建自定义界面,是否还有其他方法可以实现这一目标?

5 个答案:

答案 0 :(得分:27)

您可以始终以这种方式使用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检查是否只有一个匹配,在我的情况下(以及在您的代码中),您返回第一个匹配而不检查另一个匹配。

BTW,如果你想要实现SingleOrDefault,那么你需要改变它:

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

进入

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

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

答案 1 :(得分:19)

如果你想在数组中找到一个项目,我建议你使用ES6 find method,如下:

select u.*, b.numbuyer, s.numseller
from users u left join
     (select buyer, count(*) as numbuyer
      from transactions
      group by buyer
     ) b
     on  b.buyer = u.user_id left join
     (select seller, count(*) as numseller
      from transactions
      group by seller
     ) s
     on s.buyer = u.user_id;

它更快更容易阅读,因为您不需要写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 }

顺便说一句,如果C#collection[0]找到多个元素,它会引发异常。在这里,您尝试进行SingleOrDefault扩展。

答案 2 :(得分:0)

现在,有一个库可以在打字稿中提供强类型的可查询集合。

该库称为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(); 

答案 3 :(得分:0)

如果不需要重用,则可以通过应用简单的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);

答案 4 :(得分:0)

你可以使用reduce:

// add as prototype
Array.prototype.singleOrDefault = function(filter) {
  return this.reduce(function(current, next) {
    if (filter && !filter(next))
      return current;

    if (current)
      throw 'The input sequence contains more than one element';

    return next;
  }, null);
}

// test cases
const input = [1, 2, 3, 4, 5, 6, 1];
console.log("input: " + input);
console.log("Test 1 (=4 => found): " + input.singleOrDefault(m => m == 4));
try {
  input.singleOrDefault(m => m == 1);
}
catch(err) {
  console.error("Test 2 (=1 => error): " + err);
}
try {
  input.singleOrDefault();
}
catch(err) {
  console.error("Test 3 (without filter => error): " + err);
}
console.log("Test 4 (one item + without filter => found): " + [10].singleOrDefault());