使用 ramda 在嵌套数组中查找具有给定 id 的对象,获取除子数组之外的所有属性
Find object with given id in nested arrays with ramda, get all properties except children array
我有一个带有嵌套数组的对象,就像这样
const obj = {
id: 1,
name: "first",
children: [
{
id: 2,
name: "second",
children: []
},
{
id: 3,
name: "third",
children: [
{
id: 4,
name: "fourth",
children: []
}
]
}
]
};
我需要用 ramda 构建一个函数来递归迭代它并找到具有特定 "id" 属性 的内部对象,然后省略它的子对象。
console.log(findById(1, obj)); // => {id: 1, name: "first"}
console.log(findById(2, obj)); // => {id: 2, name: "second"}
console.log(findById(3, obj)); // => {id: 3, name: "third"}
console.log(findById(4, obj)); // => {id: 4, name: "fourth"}
我当前的解决方案看起来很丑,并且无法按预期工作
function findById(id, obj) {
if (R.propEq("id", id)(obj)) {
return R.omit(["children"])(obj);
}
if (!R.isEmpty(R.prop("children", obj))) {
return R.map((obj) => findById(id, obj), R.prop("children", obj));
}
}
> Object {id: 1, name: "first"}
> [Object]
> [Array[2]]
> [Array[2]]
这是一个使用递归和两个 Ramda 函数的解决方案:
- 一个是谓词函数 - 它检查对象是否具有所需的 属性
- 第二个是变换函数——它变换对象(省略
children
)
这两个函数可能也可以是普通的JavaScript。 (我把这个留给你作为练习。)
该函数接受一个对象列表:
- 如果第一个对象满足谓词,则转换它并return它。
- 否则在新列表上重新应用该函数,该列表可以是第一个对象子对象(如果有)或原始列表的其余部分。 (按此顺序。)
- 如果没有对象满足谓词
,特殊符号Empty
表示递归结束
正如 Scott 建议的那样,您可以使用该函数构建另一个更符合您需求的函数:
const findById = (id, obj) =>
findWith(propEq('id', id), omit(['children']), [obj]);
const Empty = Symbol();
// General all-purpose function
const findWith = (predicate, transform, [first = Empty, ...rest]) =>
first === Empty
? null
: predicate(first)
? transform(first)
: findWith(predicate, transform, first.children) ||
findWith(predicate, transform, rest);
// Your specific function
const findById = (id, obj) =>
findWith(propEq('id', id), omit(['children']), [obj]);
// found
console.log(findById(1, obj));
console.log(findById(2, obj));
console.log(findById(3, obj));
console.log(findById(4, obj));
// not found
console.log(findById(0, obj));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {propEq, omit} = R;</script>
<script>
const obj = {
id: 1,
name: "first",
children: [
{
id: 2,
name: "second",
children: []
},
{
id: 3,
name: "third",
children: [
{
id: 4,
name: "fourth",
children: []
}
]
}
]
};
</script>
我喜欢 customcommander 的解决方案(尤其是在我的建议被采纳之后!;-))但我想指出一个替代方案,使用相互递归并将转换函数与查找分开。相互递归在这里可能有点矫枉过正,但这是一个需要记住的有用技术:
const findWithArr = (pred, [x = undefined, ...xs]) =>
x == undefined
? null
: findWithObj(pred, x) || findWithArr(pred, xs)
const findWithObj = (pred, x) =>
pred(x)
? x
: findWithArr(pred, x.children || [])
const findAndTransform = (pred, transform, obj, res = findWithObj (pred, obj)) =>
res && transform (res)
const findById = (id, obj) =>
findAndTransform(propEq('id', id), omit(['children']), obj)
const obj = {id: 1, name: "first", children: [{id: 2, name: "second", children: []}, {id: 3, name: "third", children: [{id: 4, name: "fourth", children: []}]}]};
// Found
console .log (findById (1, obj))
console .log (findById (2, obj))
console .log (findById (3, obj))
console .log (findById (4, obj))
// Not found
console .log (findById (5, obj))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {propEq, omit} = R </script>
根据我的心情,我可能还会再崩溃findAndTransform
一点:
const transformNonNull = (transform, val) =>
val && transform(val)
const findAndTransform = (pred, transform, obj) =>
transformNonNull (transform, findWithObj (pred, obj))
我有一个带有嵌套数组的对象,就像这样
const obj = {
id: 1,
name: "first",
children: [
{
id: 2,
name: "second",
children: []
},
{
id: 3,
name: "third",
children: [
{
id: 4,
name: "fourth",
children: []
}
]
}
]
};
我需要用 ramda 构建一个函数来递归迭代它并找到具有特定 "id" 属性 的内部对象,然后省略它的子对象。
console.log(findById(1, obj)); // => {id: 1, name: "first"}
console.log(findById(2, obj)); // => {id: 2, name: "second"}
console.log(findById(3, obj)); // => {id: 3, name: "third"}
console.log(findById(4, obj)); // => {id: 4, name: "fourth"}
我当前的解决方案看起来很丑,并且无法按预期工作
function findById(id, obj) {
if (R.propEq("id", id)(obj)) {
return R.omit(["children"])(obj);
}
if (!R.isEmpty(R.prop("children", obj))) {
return R.map((obj) => findById(id, obj), R.prop("children", obj));
}
}
> Object {id: 1, name: "first"}
> [Object]
> [Array[2]]
> [Array[2]]
这是一个使用递归和两个 Ramda 函数的解决方案:
- 一个是谓词函数 - 它检查对象是否具有所需的 属性
- 第二个是变换函数——它变换对象(省略
children
)
这两个函数可能也可以是普通的JavaScript。 (我把这个留给你作为练习。)
该函数接受一个对象列表:
- 如果第一个对象满足谓词,则转换它并return它。
- 否则在新列表上重新应用该函数,该列表可以是第一个对象子对象(如果有)或原始列表的其余部分。 (按此顺序。)
- 如果没有对象满足谓词 ,特殊符号
Empty
表示递归结束
正如 Scott 建议的那样,您可以使用该函数构建另一个更符合您需求的函数:
const findById = (id, obj) =>
findWith(propEq('id', id), omit(['children']), [obj]);
const Empty = Symbol();
// General all-purpose function
const findWith = (predicate, transform, [first = Empty, ...rest]) =>
first === Empty
? null
: predicate(first)
? transform(first)
: findWith(predicate, transform, first.children) ||
findWith(predicate, transform, rest);
// Your specific function
const findById = (id, obj) =>
findWith(propEq('id', id), omit(['children']), [obj]);
// found
console.log(findById(1, obj));
console.log(findById(2, obj));
console.log(findById(3, obj));
console.log(findById(4, obj));
// not found
console.log(findById(0, obj));
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.min.js"></script>
<script>const {propEq, omit} = R;</script>
<script>
const obj = {
id: 1,
name: "first",
children: [
{
id: 2,
name: "second",
children: []
},
{
id: 3,
name: "third",
children: [
{
id: 4,
name: "fourth",
children: []
}
]
}
]
};
</script>
我喜欢 customcommander 的解决方案(尤其是在我的建议被采纳之后!;-))但我想指出一个替代方案,使用相互递归并将转换函数与查找分开。相互递归在这里可能有点矫枉过正,但这是一个需要记住的有用技术:
const findWithArr = (pred, [x = undefined, ...xs]) =>
x == undefined
? null
: findWithObj(pred, x) || findWithArr(pred, xs)
const findWithObj = (pred, x) =>
pred(x)
? x
: findWithArr(pred, x.children || [])
const findAndTransform = (pred, transform, obj, res = findWithObj (pred, obj)) =>
res && transform (res)
const findById = (id, obj) =>
findAndTransform(propEq('id', id), omit(['children']), obj)
const obj = {id: 1, name: "first", children: [{id: 2, name: "second", children: []}, {id: 3, name: "third", children: [{id: 4, name: "fourth", children: []}]}]};
// Found
console .log (findById (1, obj))
console .log (findById (2, obj))
console .log (findById (3, obj))
console .log (findById (4, obj))
// Not found
console .log (findById (5, obj))
<script src="//cdnjs.cloudflare.com/ajax/libs/ramda/0.26.1/ramda.js"></script>
<script>const {propEq, omit} = R </script>
根据我的心情,我可能还会再崩溃findAndTransform
一点:
const transformNonNull = (transform, val) =>
val && transform(val)
const findAndTransform = (pred, transform, obj) =>
transformNonNull (transform, findWithObj (pred, obj))