使用 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 函数的解决方案:

  1. 一个是谓词函数 - 它检查对象是否具有所需的 属性
  2. 第二个是变换函数——它变换对象(省略children

这两个函数可能也可以是普通的JavaScript。 (我把这个留给你作为练习。)

该函数接受一个对象列表:

  1. 如果第一个对象满足谓词,则转换它并return它。
  2. 否则在新列表上重新应用该函数,该列表可以是第一个对象子对象(如果有)或原始列表的其余部分。 (按此顺序。)
  3. 如果没有对象满足谓词
  4. ,特殊符号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))