如何展平嵌套的forEach循环?

4

我有以下输入:

 schema: [{
    fields: [{
      name: 'name',
      type: 'text',
      col: 10,
      value: ''
    }, {
      name: 'description',
      type: 'text',
      col: 2,
      value: ''
    }]
  }, {
    fields: [{
      name: 'password',
      type: 'text',
      col: 8,
      value: ''
    }, {
      name: 'confirmPassword',
      type: 'textarea',
      col: 4,
      value: ''
    }]
  }],

我将嵌套数组对象的values设置为以下内容:

updateField (name, value) {
  this.schema.forEach((formGroup, index) => {
    formGroup.fields.forEach(field => {
      if (field.name === name) field.value = value
    })
  })
},

有没有一种方法可以避免使用两个嵌套的 forEach?(不使用任何类库,如 Lodash?)


是的,将匿名回调函数移到外面,声明一个具名函数。 - le_m
1
你需要嵌套数组吗?在我看来,如果你将输入格式更改为一个单独的“fields”数组,那么它将更容易处理。(如果你无法更改它的来源,你能在开始处理它之前更改它吗?) - nnnnnn
4个回答

4
你可以使用reduce和扩展运算符将你的数组组合起来,然后对你的扁平数组进行forEach操作:
updateField (name, value) {
  this.schema
    .reduce((memo, item) => [...memo, ...item.fields], [])
    .forEach(field => {
      if (field.name === name) field.value = value;
    });
}

是的,这将更新原始数组中的值,同时保持原始数组的结构完整。这就是我理解问题意图的方式。然而,它可以很容易地被修改为根本不改变原始数组,或者完全改变整个数组结构。一切都取决于所需的结果。 - Eeks33
真的吗?也许我错了,但我的 JSFiddle 显示原始数组没有改变 https://jsfiddle.net/vetc7rko/ - taile
您缺少名称和值变量。这是一个传入期望名称和值的方法调用。由于您的名称未定义,因此您的if条件从未执行更新名称。请参见我的更新后的fiddle: https://jsfiddle.net/v1paxc4y/1/ - Eeks33
4
值得指出的是,尽管这个解决方案解决了嵌套回调函数的问题,但在复杂度方面它要显著差一些。时间复杂度实际上是立方的,因为每次 ... 都会完整地遍历数组。这也意味着它使用更多的内存,因为 reduce 中的每次迭代都会创建一个新的数组。话虽如此,如果数据量较小,它并不会成为真正的问题,而且看起来更加函数式,从而更加优雅 :) - nem035

3

当然可以,但实际上这并没有什么好处。

你当前的数据结构需要对数组中的每个子项进行迭代。

如果只是嵌套的外观让你感到困扰,你可以将函数分开。

updateField (name, value) {
  const updateFormGroupField = field => {
     if (field.name === name) field.value = value;
  };

  const updateFormGroup = formGroup => formGroup.forEach(updateFormGroupField);

  this.schema.forEach(updateFormGroup)
}

话虽如此,这里除了个人喜好外并没有任何好处。

也许更好的关注点是看如何使用常量访问结构(例如MapSet)来减少这些嵌套操作的二次复杂度。


我认为有一些好处:可以重复使用、进行测试和适当记录(例如JSDoc)命名回调函数(如果它被移动到updateField之外的自己的声明中)。 - le_m
当然可以。但是我认为由于其大小,这并不适用于此特定的用例。 - nem035

1
你可以使用for..of循环,对象解构,Object.entries()函数。

const obj = {
  schema: [{
    fields: [{
      name: 'name',
      type: 'text',
      col: 10,
      value: ''
    }, {
      name: 'description',
      type: 'text',
      col: 2,
      value: ''
    }]
  }, {
    fields: [{
      name: 'password',
      type: 'text',
      col: 8,
      value: ''
    }, {
      name: 'confirmPassword',
      type: 'textarea',
      col: 4,
      value: ''
    }]
  }],
  updateField(name, value) {
    for (let [key, {fields}] of Object.entries(this.schema)) {
      for (let [index, {name: _name}] of Object.entries(fields)) {
        if (name === _name) fields[index].value = value;
      }
    }
  }
}

obj.updateField("name", 123);

console.log(obj.schema[0].fields[0]);


0

我建议引入一个生成器函数,以便更轻松地迭代字段:

function* fields(schema) {
  for (let group of schema) yield* group.fields;
}

function updateField(name, value) {
  for (let field of fields(schema)) {
    if (field.name === name) field.value = value
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接