如何仅在 javascript 中执行相当于 LINQ SelectMany() 的操作
Posted
技术标签:
【中文标题】如何仅在 javascript 中执行相当于 LINQ SelectMany() 的操作【英文标题】:How to do equivalent of LINQ SelectMany() just in javascript 【发布时间】:2016-02-14 06:34:03 【问题描述】:不幸的是,我没有 JQuery 或 Underscore,只有纯 javascript(兼容 IE9)。
我想要 LINQ 功能中的 SelectMany() 等效项。
// SelectMany flattens it to just a list of phone numbers.
IEnumerable<PhoneNumber> phoneNumbers = people.SelectMany(p => p.PhoneNumbers);
我可以吗?
编辑:
感谢回答,我得到了这个工作:
var petOwners =
[
Name: "Higa, Sidney", Pets: ["Scruffy", "Sam"]
,
Name: "Ashkenazi, Ronen", Pets: ["Walker", "Sugar"]
,
Name: "Price, Vernette", Pets: ["Scratches", "Diesel"]
,
];
function property(key)return function(x)return x[key];
function flatten(a,b)return a.concat(b);
var allPets = petOwners.map(property("Pets")).reduce(flatten,[]);
console.log(petOwners[0].Pets[0]);
console.log(allPets.length); // 6
var allPets2 = petOwners.map(function(p) return p.Pets; ).reduce(function(a, b) return a.concat(b); ,[]); // all in one line
console.log(allPets2.length); // 6
【问题讨论】:
这一点也不不幸。纯 JavaScript 是惊人的。没有上下文,很难理解您在这里想要实现的目标。 @SterlingArcher,看看答案有多具体。没有太多可能的答案,最好的答案简短明了。 【参考方案1】:对于简单的选择,您可以使用 Array 的 reduce 函数。 假设你有一个数字数组:
var arr = [[1,2],[3, 4]];
arr.reduce(function(a, b) return a.concat(b); , []);
=> [1,2,3,4]
var arr = [ name: "name1", phoneNumbers : [5551111, 5552222], name: "name2",phoneNumbers : [5553333] ];
arr.map(function(p) return p.phoneNumbers; )
.reduce(function(a, b) return a.concat(b); , [])
=> [5551111, 5552222, 5553333]
编辑:
由于 es6 flatMap 已添加到 Array 原型中。
SelectMany
是 flatMap
的同义词。
该方法首先使用映射函数映射每个元素,然后将结果展平为新数组。
它在 TypeScript 中的简化签名是:
function flatMap<A, B>(f: (value: A) => B[]): B[]
为了完成任务,我们只需要将每个元素 flatMap 到 phoneNumbers
arr.flatMap(a => a.phoneNumbers);
【讨论】:
最后一个也可以写成arr.reduce(function(a, b) return a.concat(b.phoneNumbers); , [])
您不需要使用 .map() 或 .concat()。请参阅下面的答案。
这不会改变原始数组吗?
现在不那么重要了,但flatMap
不符合 OP 对 IE9 兼容解决方案的要求 -- browser compat for flatmap
。
reduce() 在空数组上失败,因此您必须添加一个初始值。见***.com/questions/23359173/…【参考方案2】:
作为一个更简单的选项Array.prototype.flatMap() 或Array.prototype.flat()
const data = [
id: 1, name: 'Dummy Data1', details: [id: 1, name: 'Dummy Data1 Details', id: 1, name: 'Dummy Data1 Details2'],
id: 1, name: 'Dummy Data2', details: [id: 2, name: 'Dummy Data2 Details', id: 1, name: 'Dummy Data2 Details2'],
id: 1, name: 'Dummy Data3', details: [id: 3, name: 'Dummy Data3 Details', id: 1, name: 'Dummy Data3 Details2'],
]
const result = data.flatMap(a => a.details); // or data.map(a => a.details).flat(1);
console.log(result)
【讨论】:
简洁明了。到目前为止最好的答案。 根据 MDN,截至 2019 年 12 月 4 日,flat() 在 Edge 中不可用。 但是新的 Edge(基于 Chromium)会支持它。 看来Array.prototype.flatMap()也是一个东西,所以你的例子可以简化为const result = data.flatMap(a => a.details)
【参考方案3】:
稍后,了解 javascript 但仍希望在 Typescript 中使用简单的 Typed SelectMany 方法:
function selectMany<TIn, TOut>(input: TIn[], selectListFn: (t: TIn) => TOut[]): TOut[]
return input.reduce((out, inx) =>
out.push(...selectListFn(inx));
return out;
, new Array<TOut>());
【讨论】:
【参考方案4】:Sagi 使用 concat 方法来展平数组是正确的。但是要获得与此示例类似的内容,您还需要选择部分的地图 https://msdn.microsoft.com/library/bb534336(v=vs.100).aspx
/* arr is something like this from the example PetOwner[] petOwners =
new PetOwner Name="Higa, Sidney",
Pets = new List<string> "Scruffy", "Sam" ,
new PetOwner Name="Ashkenazi, Ronen",
Pets = new List<string> "Walker", "Sugar" ,
new PetOwner Name="Price, Vernette",
Pets = new List<string> "Scratches", "Diesel" ; */
function property(key)return function(x)return x[key];
function flatten(a,b)return a.concat(b);
arr.map(property("pets")).reduce(flatten,[])
【讨论】:
我';要做一个小提琴;我可以将您的数据用作 json。将尝试将您的答案扁平化为一行代码。 “如何扁平化关于扁平化对象层次结构的答案”lol 随意使用您的数据...我明确抽象了 map 函数,以便您可以轻松选择任何属性名称,而无需每次都编写新函数。只需将arr
替换为people
并将"pets"
替换为"PhoneNumbers"
用扁平化版本编辑了我的问题,并对你的答案投了赞成票。谢谢。
辅助函数会清理东西,但是使用 ES6 你可以这样做:petOwners.map(owner => owner.Pets).reduce((a, b) => a.concat(b), []);
。或者,更简单的是petOwners.reduce((a, b) => a.concat(b.Pets), []);
。【参考方案5】:
// you can save this function in a common js file of your project
function selectMany(f)
return function (acc,b)
return acc.concat(f(b))
var ex1 = [items:[1,2],items:[4,"asda"]];
var ex2 = [[1,2,3],[4,5]]
var ex3 = []
var ex4 = [nodes:["1","v"]]
开始吧
ex1.reduce(selectMany(x=>x.items),[])
=> [1, 2, 4, "asda"]
ex2.reduce(selectMany(x=>x),[])
=> [1, 2, 3, 4, 5]
ex3.reduce(selectMany(x=> "this will not be called" ),[])
=> []
ex4.reduce(selectMany(x=> x.nodes ),[])
=> ["1", "v"]
注意:在 reduce 函数中使用有效数组(非 null)作为初始值
【讨论】:
【参考方案6】:试试这个(使用 es6):
Array.prototype.SelectMany = function (keyGetter)
return this.map(x=>keyGetter(x)).reduce((a, b) => a.concat(b));
示例数组:
var juices=[
key:"apple",data:[1,2,3],
key:"banana",data:[4,5,6],
key:"orange",data:[7,8,9]
]
使用:
juices.SelectMany(x=>x.data)
【讨论】:
【参考方案7】:我会这样做(避免使用 .concat()):
function SelectMany(array)
var flatten = function(arr, e)
if (e && e.length)
return e.reduce(flatten, arr);
else
arr.push(e);
return arr;
;
return array.reduce(flatten, []);
var nestedArray = [1,2,[3,4,[5,6,7],8],9,10];
console.log(SelectMany(nestedArray)) //[1,2,3,4,5,6,7,8,9,10]
如果你不想使用 .reduce():
function SelectMany(array, arr = [])
for (let item of array)
if (item && item.length)
arr = SelectMany(item, arr);
else
arr.push(item);
return arr;
如果你想使用 .forEach():
function SelectMany(array, arr = [])
array.forEach(e =>
if (e && e.length)
arr = SelectMany(e, arr);
else
arr.push(e);
);
return arr;
【讨论】:
我觉得很有趣,我在 4 年前问过这个问题,然后弹出堆栈溢出通知给你的答案,而我此时正在做的事情是与 js 数组作斗争。很好的递归! 我很高兴有人看到它!我很惊讶我写的东西还没有列出,考虑到线程已经持续了多长时间以及有多少尝试的答案。 @toddmo 对于它的价值,如果你现在正在处理 js 数组,你可能会对我最近在这里添加的解决方案感兴趣:***.com/questions/1960473/…。【参考方案8】:这里是 joel-harkes 在 TypeScript 中的答案的重写版本,作为扩展,可用于任何数组。所以你可以像somearray.selectMany(c=>c.someprop)
一样使用它。转堆,这是javascript。
declare global
interface Array<T>
selectMany<TIn, TOut>(selectListFn: (t: TIn) => TOut[]): TOut[];
Array.prototype.selectMany = function <TIn, TOut>( selectListFn: (t: TIn) => TOut[]): TOut[]
return this.reduce((out, inx) =>
out.push(...selectListFn(inx));
return out;
, new Array<TOut>());
export ;
【讨论】:
【参考方案9】:您可以尝试实现所有 C# LINQ 方法并保留其语法的 manipula
包:
Manipula.from(petOwners).selectMany(x=>x.Pets).toArray()
https://github.com/litichevskiydv/manipula
https://www.npmjs.com/package/manipula
【讨论】:
不错的解决方案!【参考方案10】:对于更高版本的 JavaScript,您可以这样做:
var petOwners = [
Name: 'Higa, Sidney',
Pets: ['Scruffy', 'Sam']
,
Name: 'Ashkenazi, Ronen',
Pets: ['Walker', 'Sugar']
,
Name: 'Price, Vernette',
Pets: ['Scratches', 'Diesel']
];
var arrayOfArrays = petOwners.map(po => po.Pets);
var allPets = [].concat(...arrayOfArrays);
console.log(allPets); // ["Scruffy","Sam","Walker","Sugar","Scratches","Diesel"]
见example StackBlitz。
【讨论】:
以上是关于如何仅在 javascript 中执行相当于 LINQ SelectMany() 的操作的主要内容,如果未能解决你的问题,请参考以下文章