将未知长度的数组减少为嵌套对象

时间:2018-11-27 23:13:31

标签: javascript

我试图获取一个数组并从中创建一个嵌套对象,其中数组中的每个项目都是上一个项目的属性。

我认为reduce是做到这一点的方法,但是我发现reduce很难掌握,而且我尝试做的所有事情都卡住了,因为我不知道该如何进入下一个层次。 JS: Reduce array to nested objects是一个类似的问题,但是尝试了许多变体后,我仍然无法使用。

const myArray = ['one', 'two', 'three'];

// Intended Output (note, the staticCount is always 1)
{
    one: {
        staticCount: 1,

        two: {
            staticCount: 1,

            three: {
                staticCount: 1
            }
        }
    }
}

6 个答案:

答案 0 :(得分:5)

某些工作需要Array.prototype.reduceRight

const myArray = ['one', 'two', 'three']

const nestNode = (acc, key) => {
  acc.staticCount = 1
  return { [key]: acc }
}
console.log(myArray.reduceRight(nestNode, {}))

让我们看一下reduceRight(以及扩展名reduce):

我将迭代器函数定义移出了对reduceRight的调用,以使该示例更易于讨论(请参见nestNode)。

reducereduceRight相似:

  1. 每个函数都有两个参数,一个迭代器函数和一个该函数的累加器的初始值。第二个参数是可选的,但在这里我将忽略它。

  2. 每个函数都会迭代调用它们的数组中的所有项目,并使用四个参数(累加器,数组中的当前项目,当前迭代)为数组中的每个项目调用迭代器函数计数以及调用了reduce的整个数组。最后两个参数在这里无关紧要(我很少使用它们)。

  3. 首次调用迭代器函数时,它将传递给您提供给reducereduceRight(初始累加器值)的第二个参数。然后,它将传递上一步中迭代器函数返回的内容。

因为我认为reduce(以及扩展名reduceRight)是值得理解的功能强大的抽象,所以我将逐步介绍代码示例中的前两个步骤:

  1. 在迭代的第一步,我们的迭代器函数将这样调用:nestNode(acc = {}, key = 'three')。在nestNode内,我们向staticCount添加一个acc属性,并将其设置为1,得到acc = { staticCount: 1 }。然后,我们创建并返回一个名为'three'的属性,其值等于acc的新对象。 nestNode在第一步中返回的值为{ three: { staticCount: 1 } },在第二步中将用该值调用nestNode

  2. 在迭代的第二步中,我们的迭代器函数将这样调用:nestNode(acc = { three: { staticCount: 1 } }, key = 'two')。同样,我们将staticCount属性添加到acc并将其设置为1,从而得到acc = { three: { staticCount: 1 }, staticCount: 1 }。然后,我们创建并返回一个名为'two'的属性,其值等于acc的新对象。我们返回的值为{ two: { three: { staticCount: 1 }, staticCount: 1 } }。同样,此值将在下一步中使用。

我将跳过最后一步,因为我希望详细了解一下前两个步骤足以使事情变得简单。如果您还有其他问题,或者仍然不清楚或感到困惑,请告诉我。

reduce(和reduceRight)是功能强大且灵活的工具,值得学习和使用。

作为尾声,我将在每一步之后为您提供迭代器函数的返回值:

  1. { three: { staticCount: 1 } }

  2. { two: { three: { staticCount: 1 } }, staticCount: 1 }

  3. { one: { two: { three: { staticCount: 1 } }, staticCount: 1 }, staticCount: 1 }

答案 1 :(得分:2)

感谢Tex使我将reverse().reduce()替换为reduceRight

['one', 'two', 'three'].reduceRight((a, c) => ({[c]: { staticCount: 1, ...a }}), {});

答案 2 :(得分:2)

reducemap一样,将循环访问数组中的每个项目并返回结果。关键区别在于map将返回与原始大小相等的数组,并且您进行了任何修改。 reduce采用所谓的accumulator并将其作为最终结果返回。

reduce()具有两个参数:

  1. 一个function()
  2. accumulator的起始值

您提供的function()具有三个值:

  1. accumulator的值
  2. 数组中当前项目的值
  3. 当前迭代的值(在此示例中未使用)
  4. 原始数组的值(在此示例中未使用)

关于accumulator的最重要的了解是,它将变成您的function()返回值的值,而您的函数 ALWAYS 必须返回某些值,否则accumulator在下一个循环中将是未定义的。

遵循问题的解决方案是使用reduce的基本示例。

解决方案

const myArray = ['one','two','three'];
const result = {};

myArray.reduce((accumulator, num) => {
  accumulator[num] = { staticCount: 1}
  return accumulator[num];
}, result);

console.log(result);

性能

此处提供的reduce解决方案每秒可执行760万次操作,而reduceRight每秒可执行220万次操作。

https://jsperf.com/reduceright-vs-reduce/

基本示例

var numbers = [1, 2, 3, 4, 5];

// Reduce will assign sum whatever
// the value of result is on the last loop

var sum = numbers.reduce((result, number) => {
  return result + number;
}, 0); // start result at 0

console.log(sum);

另一个例子

var numbers = [1, 2, 3, 4, 5];

// Here we're using the iterator, and
// assinging "too much" to sum if there
// are more than 4 numbers.

var sum = numbers.reduce((result, number, i) => {
  if (i >= 4) return "too much";
  return result + number;
}, 0);

console.log(sum);

文档

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/Reduce

答案 3 :(得分:1)

只使用减少。之所以有效,是因为对象是通过引用传递的。

const myArray = ['one', 'two', 'three'];
const newObject = {};

myArray.reduce((acummulator, element) => {
  acummulator[element] = {
    staticCount: 1
  };
  return acummulator[element];
}, newObject);

// Intended Output (note, the staticCount is always 1)

console.log(newObject);

了解有关减少here的信息。

答案 4 :(得分:1)

诀窍不是从一个空的对象开始,而是从一些声明的变量开始。然后只需将新的子项作为下一个递归的集合向下传递。

参考文献已更新,您可以再次打印根目录。

const myArray = ['one', 'two', 'three'];
const root = {};
myArray.reduce( (agg, c) => {
  agg[c] = { staticCount: 1 };
  return agg[c];
}, root );

console.log( root );

答案 5 :(得分:1)

这也可以通过递归函数来实现:

const createObj = (keys) => keys.length > 0 && ({
  [keys[0]]: {
    staticCount: 1,
    ...createObj(keys.slice(1))
  }
});

console.log(createObj(['one', 'two', 'three']));