给定数组提取对象值的功能方法
Functional way to extract object values given an array
我有一个键值对对象数组,如下所示:
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
和一个数组
var a = ['a','e']
如何编写提取值的函数,本质上我想要的结果是
[{'name':'a', 'value':1},{'name':'e', 'value':5} ]
我想以纯函数的方式编写它,而不是使用 for
、while
循环或 filter
函数。请问有没有合适有效的方法呢?递归是正确的方法吗?我愿意使用 lodash
、ramda
或任何其他 FP 包。
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}];
var a = ['a','e'];
d.filter(function(item){
return a.indexOf(item.name) > -1;
})
// will return [{'name':'a', 'value':1}, {'name':'b', 'value':5}]
希望这对您有所帮助
如果你希望你的解决方案是递归的,你会想要这样的东西。
function getValues(objects, names, result) {
if (objects.length === 0 || names.length === 0) {
return result;
}
var index = names.indexOf(objects[0].name)
if (index > -1) {
result.push(objects[0]);
return getValues(objects.slice(1), [...names.slice(0, index), ...names.slice(index, names.length)], result);
} else {
return getValues(objects.slice(1), names, result);
}
}
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
var a = ['a','e']
console.log(getValues(d, a, []));
我认为 OP 正在寻找这样的东西:
const _ = require('lodash');
function solve(array, keys) {
if (_.isEmpty(array) || _.isEmpty(keys)) {
return [];
} else {
const [ele, ...tailArray] = array;
const [key, ...tailKeys] = keys;
if (ele.name === key) {
return [ele, ...solve(tailArray, tailKeys)];
} else {
return solve(tailArray, keys);
}
}
}
const d = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}, {'name': 'c', 'value': 3}, {'name': 'd', 'value': 4}, {'name': 'e', 'value': 5}];
const a = ['a', 'e'];
console.log(solve(d, a));
这假设两个数组都已排序,所有名称都是唯一的,并且 array a
中的所有元素也存在于 array d
中。其他解决方案留作@cherhan 的练习。
Array.prototype.map 和 Array.prototype.find
看来您对实现细节很固执,但我敢肯定您不知道为什么。 for
或 while
或 filter
都没有错。到这篇post结束时,我不希望你相信我,而是希望你知道是真的。
您可以使用 map
和 find
轻松解决此问题 – 对于所有意图和目的,如何 map
和 find
实现对你来说是个谜。他们使用 for
循环甚至 GOTO
来大声喊叫都没有关系。对您来说重要的是您可以用实用的方式表达您的问题。
map
和 find
是谦逊的 JavaScript 函数式程序员的完美伴侣...
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
var a = ['a','e']
var result = a.map(x => d.find(y => y.name === x))
console.log(result)
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
泛型函数促进代码重用
使用一些通用函数,我们可以用更可重用的方式表达解决方案
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const findBy = k => xs => v =>
xs.find(x => x[k] === v)
const findAllBy = k => xs => vs =>
vs.map(findBy(k)(xs))
console.log(findAllBy ('name') (data) (['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
为什么固执无济于事
如果你想看看这是如何不依赖any for
, while
, filter
, map
,或find
...
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const main = ([x,...xs]) => {
const aux = ([y,...ys]) => {
if (y === undefined)
return null
else if (y.name === x)
return y
else
return aux (ys)
}
if (x === undefined)
return []
else
return [aux(data), ...main(xs)]
}
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
很痛苦吧?与这里的其他答案相比,该代码的进展要少一些,这些答案仍然具有 length
、indexOf
、slice
或整个 lodash 库等依赖项。
所以这样做真的没有意义。此示例中编码了大量有用的通用函数,我们希望在其他地方使用它们。这就是为什么像 map
、reduce
、filter
和 find
这样的函数首先存在的原因——for
和 while
也是如此。
每次我想遍历一个数组,或者过滤一个数组,或者在数组中查找一个元素时,我都不想手动完成。我使用函数的原因是我不必重复自己......永远。
好奇心
所以也许您对上面的 map
+find
解决方案没有问题,但您也很好奇如果您没有它们,您将如何自己实施它们您已经完成了处置——下面只是 无数 种方法中的两种,您可以通过这些方法实现这些功能中的任何一个。
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const find = f => ([x,...xs]) =>
x === undefined ? null :
f(x) === true ? x : find (f) (xs)
const map = f => ([x,...xs]) =>
x === undefined ? [] : [f(x), ...map(f)(xs)]
const main =
map (x => find (y => y.name === x) (data))
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
递归陷入困境 JavaScript
很多人不了解 JavaScript 中的递归是它实际上不是很好。我们被承诺在 ES6 中消除尾调用,但目前 JavaScript 的实现还没有真正支持它。这意味着您编写的任何递归函数(除非您对其进行蹦床)都存在堆栈溢出的潜在风险。
这意味着使用 for
和 while
实现功能接口比 所以 好得多。
下面,我们重新实现 map
和 find
但这次我们通过避免堆栈溢出的风险来巧妙地实现它。请注意,它实际上对生成的代码没有影响。我们仍然可以以一种很好的、实用的方式与 map
和 find
交互——它们都是引用透明的
请特别注意 main
函数 - 它与上面的示例完全没有变化。我们的 "ugly" while
和 for
循环作为实现细节被巧妙地隐藏起来,最终用户 none 更明智。
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const find = f => xs => {
for (let x of xs)
if (f(x) === true)
return x
return null
}
const map = f => xs => {
const acc = Array(xs.length)
for (let [i,x] of xs.entries())
acc[i] = f(x)
return acc
}
const main =
map (x => find (y => y.name === x) (data))
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
不幸的是我不得不使用 R.map
,但是 Ramda
:
R.flatten(R.map(R.useWith(R.filter, [R.propEq('name'), R.identity])(R.__, d), a))
这是我能想到的,但我知道有更好的解决方案,我很想听听
编辑:我觉得这个更好
R.filter(R.compose(R.flip(R.contains)(a), R.prop('name')), d)
我有一个键值对对象数组,如下所示:
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
和一个数组
var a = ['a','e']
如何编写提取值的函数,本质上我想要的结果是
[{'name':'a', 'value':1},{'name':'e', 'value':5} ]
我想以纯函数的方式编写它,而不是使用 for
、while
循环或 filter
函数。请问有没有合适有效的方法呢?递归是正确的方法吗?我愿意使用 lodash
、ramda
或任何其他 FP 包。
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}];
var a = ['a','e'];
d.filter(function(item){
return a.indexOf(item.name) > -1;
})
// will return [{'name':'a', 'value':1}, {'name':'b', 'value':5}]
希望这对您有所帮助
如果你希望你的解决方案是递归的,你会想要这样的东西。
function getValues(objects, names, result) {
if (objects.length === 0 || names.length === 0) {
return result;
}
var index = names.indexOf(objects[0].name)
if (index > -1) {
result.push(objects[0]);
return getValues(objects.slice(1), [...names.slice(0, index), ...names.slice(index, names.length)], result);
} else {
return getValues(objects.slice(1), names, result);
}
}
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
var a = ['a','e']
console.log(getValues(d, a, []));
我认为 OP 正在寻找这样的东西:
const _ = require('lodash');
function solve(array, keys) {
if (_.isEmpty(array) || _.isEmpty(keys)) {
return [];
} else {
const [ele, ...tailArray] = array;
const [key, ...tailKeys] = keys;
if (ele.name === key) {
return [ele, ...solve(tailArray, tailKeys)];
} else {
return solve(tailArray, keys);
}
}
}
const d = [{'name': 'a', 'value': 1}, {'name': 'b', 'value': 2}, {'name': 'c', 'value': 3}, {'name': 'd', 'value': 4}, {'name': 'e', 'value': 5}];
const a = ['a', 'e'];
console.log(solve(d, a));
这假设两个数组都已排序,所有名称都是唯一的,并且 array a
中的所有元素也存在于 array d
中。其他解决方案留作@cherhan 的练习。
Array.prototype.map 和 Array.prototype.find
看来您对实现细节很固执,但我敢肯定您不知道为什么。 for
或 while
或 filter
都没有错。到这篇post结束时,我不希望你相信我,而是希望你知道是真的。
您可以使用 map
和 find
轻松解决此问题 – 对于所有意图和目的,如何 map
和 find
实现对你来说是个谜。他们使用 for
循环甚至 GOTO
来大声喊叫都没有关系。对您来说重要的是您可以用实用的方式表达您的问题。
map
和 find
是谦逊的 JavaScript 函数式程序员的完美伴侣...
var d = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
var a = ['a','e']
var result = a.map(x => d.find(y => y.name === x))
console.log(result)
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
泛型函数促进代码重用
使用一些通用函数,我们可以用更可重用的方式表达解决方案
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const findBy = k => xs => v =>
xs.find(x => x[k] === v)
const findAllBy = k => xs => vs =>
vs.map(findBy(k)(xs))
console.log(findAllBy ('name') (data) (['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
为什么固执无济于事
如果你想看看这是如何不依赖any for
, while
, filter
, map
,或find
...
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const main = ([x,...xs]) => {
const aux = ([y,...ys]) => {
if (y === undefined)
return null
else if (y.name === x)
return y
else
return aux (ys)
}
if (x === undefined)
return []
else
return [aux(data), ...main(xs)]
}
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
很痛苦吧?与这里的其他答案相比,该代码的进展要少一些,这些答案仍然具有 length
、indexOf
、slice
或整个 lodash 库等依赖项。
所以这样做真的没有意义。此示例中编码了大量有用的通用函数,我们希望在其他地方使用它们。这就是为什么像 map
、reduce
、filter
和 find
这样的函数首先存在的原因——for
和 while
也是如此。
每次我想遍历一个数组,或者过滤一个数组,或者在数组中查找一个元素时,我都不想手动完成。我使用函数的原因是我不必重复自己......永远。
好奇心
所以也许您对上面的 map
+find
解决方案没有问题,但您也很好奇如果您没有它们,您将如何自己实施它们您已经完成了处置——下面只是 无数 种方法中的两种,您可以通过这些方法实现这些功能中的任何一个。
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const find = f => ([x,...xs]) =>
x === undefined ? null :
f(x) === true ? x : find (f) (xs)
const map = f => ([x,...xs]) =>
x === undefined ? [] : [f(x), ...map(f)(xs)]
const main =
map (x => find (y => y.name === x) (data))
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
递归陷入困境 JavaScript
很多人不了解 JavaScript 中的递归是它实际上不是很好。我们被承诺在 ES6 中消除尾调用,但目前 JavaScript 的实现还没有真正支持它。这意味着您编写的任何递归函数(除非您对其进行蹦床)都存在堆栈溢出的潜在风险。
这意味着使用 for
和 while
实现功能接口比 所以 好得多。
下面,我们重新实现 map
和 find
但这次我们通过避免堆栈溢出的风险来巧妙地实现它。请注意,它实际上对生成的代码没有影响。我们仍然可以以一种很好的、实用的方式与 map
和 find
交互——它们都是引用透明的
请特别注意 main
函数 - 它与上面的示例完全没有变化。我们的 "ugly" while
和 for
循环作为实现细节被巧妙地隐藏起来,最终用户 none 更明智。
var data = [{'name':'a', 'value':1}, {'name':'b', 'value':2}, {'name':'c', 'value':3}, {'name':'d', 'value':4}, {'name':'e', 'value':5}, {'name':'f', 'value':6}]
const find = f => xs => {
for (let x of xs)
if (f(x) === true)
return x
return null
}
const map = f => xs => {
const acc = Array(xs.length)
for (let [i,x] of xs.entries())
acc[i] = f(x)
return acc
}
const main =
map (x => find (y => y.name === x) (data))
console.log(main(['a', 'e']))
// [ { name: 'a', value: 1 },
// { name: 'e', value: 5 } ]
不幸的是我不得不使用 R.map
,但是 Ramda
:
R.flatten(R.map(R.useWith(R.filter, [R.propEq('name'), R.identity])(R.__, d), a))
这是我能想到的,但我知道有更好的解决方案,我很想听听
编辑:我觉得这个更好
R.filter(R.compose(R.flip(R.contains)(a), R.prop('name')), d)