什么是遍历JavaScript的最佳方法?

3

我有一个主列表,它是一个对象的嵌套数组,同时我也有一个选定的列表。

let masterList = [{
  category_id: 1,
  meta_list: [{
    name: "Cuisine",
    id: "QWEQWEQWE",
    is_multiselect: false,
    item_list: ["Arabian", "Indian"]
  }, {
    name: "Cost for Two",
    id: "SFDFSDASDASDASD",
    is_multiselect: true,
    item_list: [
      "AED 0 - 100",
      "AED 100 - 200",
      "Greater Than AED 200"
    ]
  }]
}, {
  category_id: 2,
  meta_list: [{
    name: "Cat 2",
    id: "cat2",
    is_multiselect: false,
    item_list: ["cat 2 1", "cat 2 2"]
  }, {
    name: "cuisine 2",
    id: "cui2",
    is_multiselect: true,
    item_list: ["cu1", "cu2"]
  }]
}];

let selectedList = [{
  category_id: 1,
  meta_list: [{
    name: "Cuisine",
    id: "QWEQWEQWE",
    is_multiselect: false,
    item_list: ["Arabian"]
  }, {
    name: "Cost for Two",
    id: "SFDFSDASDASDASD",
    is_multiselect: true,
    item_list: [
      "AED 100 - 200",
      "Greater Than AED 200"
    ]
  }]
}, {
  category_id: 2,
  meta_list: [{
    name: "cuisine 2",
    id: "cui2",
    is_multiselect: true,
    item_list: ["cu1"]
  }]
}];

使用主列表和选定列表,我想要一个派生列表,它必须如下所示。

[{
  category_id: 1,
  meta_list: [{
    name: "Cuisine",
    id: "QWEQWEQWE",
    is_multiselect: false,
    item_list: ["Arabian", "Indian"],
    sel_list: ["Arabian"]
  }, {
    name: "Cost for Two",
    id: "SFDFSDASDASDASD",
    is_multiselect: true,
    item_list: [
      "AED 0 - 100",
      "AED 100 - 200",
      "Greater Than AED 200"
    ],
    sel_list: [
      "AED 100 - 200",
      "Greater Than AED 200"
    ]
  }]
}, {
  category_id: 2,
  meta_list: [{
    name: "Cat 2",
    id: "cat2",
    is_multiselect: false,
    item_list: ["cat 2 1", "cat 2 2"],
    sel_list: ["cat 2 2"]
  }, {
    name: "cuisine 2",
    id: "cui2",
    is_multiselect: true,
    item_list: ["cu1", "cu2"],
    sel_list: ["cu1"]
  }]
}];

我希望得到一个名为desiredList的列表,与masterList具有相同的项目和结构,除了它将具有另一个属性sel_list,其值将是selected_list数组中的item_list对应的值。
masterList和selectedList的区别在于selectedList中的item_list属性是masterList中item_list的子集。
正如您在示例中看到的,masterList也可能有额外的项目。

{
  name: "Cat 2",
  id: "cat2",
  is_multiselect: false,
  item_list: ["cat 2 1", "cat 2 2"],
}

[{
  category_id: 1,
  meta_list: [{
    name: "Cuisine",
    id: "QWEQWEQWE",
    is_multiselect: false,
    item_list: ["Arabian", "Indian"],
    sel_list: ["Arabian"]
  }, {
    name: "Cost for Two",
    id: "SFDFSDASDASDASD",
    is_multiselect: true,
    item_list: [
      "AED 0 - 100",
      "AED 100 - 200",
      "Greater Than AED 200"
    ],
    sel_list: [
      "AED 100 - 200",
      "Greater Than AED 200"
    ]
  }]
}, {
  category_id: 2,
  meta_list: [{
    name: "Cat 2",
    id: "cat2",
    is_multiselect: false,
    item_list: ["cat 2 1", "cat 2 2"],
    sel_list: ["cat 2 2"]
  }, {
    name: "cuisine 2",
    id: "cui2",
    is_multiselect: true,
    item_list: ["cu1", "cu2"],
    sel_list: ["cu1"]
  }]
}]

这里需要排除在selectedList中的项目。

我该如何实现?

编辑:目前我的处理方法是这样的。

masterList.forEach(cat_met_item => {
  selectedList.forEach(filled_cat_met_item => {
    if (cat_met_item.category_id === filled_cat_met_item.category_id) {
      cat_met_item.meta_list.forEach(met_item => {
        filled_cat_met_item.meta_list.forEach(filled_met_item => {
          if (met_item.id === filled_met_item.id) {
            met_item["list"] = filled_met_item["item_list"];
          }
        });
      });
    }
  });
});

使用4个forEach循环。我发现我的方法效率不高,并且非常混乱,那么有没有更好的方法来做到这一点?

@CertainPerformance 我已经添加了代码片段,展示我是如何实现这个的。请再考虑一下。 - PCK
你可以使用jsonArray.filter(function (el) {return condition;})来获取所需的对象,而不是使用循环,这样可以显著减少嵌套循环。 - D. Pareek
@D.Pareek,您能演示一下我如何在上述用例中使用过滤器吗? - PCK
小提示:如果您关心性能,请考虑使用常规的for循环,它们比任何其他高阶函数(forEach、map、reduce等)都要快得多。 - mliakos
5个回答

3

使用 JSON 过滤方法:

let masterList = [
                {
                    category_id: 1,
                    meta_list: [
                        {
                            name: "Cuisine",
                            id: "QWEQWEQWE",
                            is_multiselect: false,
                            item_list: ["Arabian", "Indian"]
                        },

                        {
                            name: "Cost for Two",
                            id: "SFDFSDASDASDASD",
                            is_multiselect: true,
                            item_list: [
                                "AED 0 - 100",
                                "AED 100 - 200",
                                "Greater Than AED 200"
                            ]
                        }
                    ]
                },
                {
                    category_id: 2,
                    meta_list: [
                        {
                            name: "Cat 2",
                            id: "cat2",
                            is_multiselect: false,
                            item_list: ["cat 2 1", "cat 2 2"]
                        },

                        {
                            name: "cuisine 2",
                            id: "cui2",
                            is_multiselect: true,
                            item_list: ["cu1", "cu2"]
                        }
                    ]
                }
            ];

      let selectedList = [
                        {
                            category_id: 1,
                            meta_list: [
                                {
                                    name: "Cuisine",
                                    id: "QWEQWEQWE",
                                    is_multiselect: false,
                                    item_list: ["Arabian"]
                                },
                                {
                                    name: "Cost for Two",
                                    id: "SFDFSDASDASDASD",
                                    is_multiselect: true,
                                    item_list: [
                                        "AED 100 - 200",
                                        "Greater Than AED 200"
                                    ]
                                }
                            ]
                        },
                        {
                            category_id: 2,
                            meta_list: [
                                {
                                    name: "cuisine 2",
                                    id: "cui2",
                                    is_multiselect: true,
                                    item_list: ["cu1"]
                                }
                            ]
                        }
                    ];

masterList.forEach(cat_met_item => {
  var obj = selectedList.filter(function (el) {
    return cat_met_item.category_id === el.category_id;
    
  })
  cat_met_item.meta_list.forEach(met_item => {
      var selObj = obj[0].meta_list.filter(function (el) {
      return met_item.id === el.id;
    });
    if (selObj[0]) {
      met_item['sel_list'] = selObj[0].item_list;
    }
    else {
      met_item['sel_list'] = met_item.item_list[met_item.item_list.length - 1];
    }
  });
});

console.log(masterList);


2

在这种情况下,使用Array.prototype.find()可能更好。

最初的回答

let masterList = [
                {
                    category_id: 1,
                    meta_list: [
                        {
                            name: "Cuisine",
                            id: "QWEQWEQWE",
                            is_multiselect: false,
                            item_list: ["Arabian", "Indian"]
                        },

                        {
                            name: "Cost for Two",
                            id: "SFDFSDASDASDASD",
                            is_multiselect: true,
                            item_list: [
                                "AED 0 - 100",
                                "AED 100 - 200",
                                "Greater Than AED 200"
                            ]
                        }
                    ]
                },
                {
                    category_id: 2,
                    meta_list: [
                        {
                            name: "Cat 2",
                            id: "cat2",
                            is_multiselect: false,
                            item_list: ["cat 2 1", "cat 2 2"]
                        },

                        {
                            name: "cuisine 2",
                            id: "cui2",
                            is_multiselect: true,
                            item_list: ["cu1", "cu2"]
                        }
                    ]
                }
            ];

      let selectedList = [
                        {
                            category_id: 1,
                            meta_list: [
                                {
                                    name: "Cuisine",
                                    id: "QWEQWEQWE",
                                    is_multiselect: false,
                                    item_list: ["Arabian"]
                                },
                                {
                                    name: "Cost for Two",
                                    id: "SFDFSDASDASDASD",
                                    is_multiselect: true,
                                    item_list: [
                                        "AED 100 - 200",
                                        "Greater Than AED 200"
                                    ]
                                }
                            ]
                        },
                        {
                            category_id: 2,
                            meta_list: [
                                {
                                    name: "cuisine 2",
                                    id: "cui2",
                                    is_multiselect: true,
                                    item_list: ["cu1"]
                                }
                            ]
                        }
                    ];

masterList.forEach(master_item => {
  let select_item = selectedList.find((el) => master_item.category_id === el.category_id);
  master_item.meta_list.forEach(met_item => {
    let select_meta = select_item.meta_list.find((el) => met_item.id === el.id);
    if (select_meta) {
      met_item.sel_list = select_meta.item_list;
    } else {
      met_item.sel_list = [met_item.item_list[met_item.item_list.length - 1]];
    }
  });
});
console.log(masterList)


2
你可以使用reduce来迭代,使用find来获取所需的项。下面的代码还处理了缺少selected/meta_list项的情况。"最初的回答"。

const exampleMasterList = [
  {
    category_id: 1,
    meta_list: [
      {
        name: 'Cuisine',
        id: 'QWEQWEQWE',
        is_multiselect: false,
        item_list: ['Arabian', 'Indian']
      },

      {
        name: 'Cost for Two',
        id: 'SFDFSDASDASDASD',
        is_multiselect: true,
        item_list: ['AED 0 - 100', 'AED 100 - 200', 'Greater Than AED 200']
      }
    ]
  },
  {
    category_id: 2,
    meta_list: [
      {
        name: 'Cat 2',
        id: 'cat2',
        is_multiselect: false,
        item_list: ['cat 2 1', 'cat 2 2']
      },

      {
        name: 'cuisine 2',
        id: 'cui2',
        is_multiselect: true,
        item_list: ['cu1', 'cu2']
      }
    ]
  }
];

const exampleSelectedList = [
  {
    category_id: 1,
    meta_list: [
      {
        name: 'Cuisine',
        id: 'QWEQWEQWE',
        is_multiselect: false,
        item_list: ['Arabian']
      },
      {
        name: 'Cost for Two',
        id: 'SFDFSDASDASDASD',
        is_multiselect: true,
        item_list: ['AED 100 - 200', 'Greater Than AED 200']
      }
    ]
  },
  {
    category_id: 2,
    meta_list: [
      {
        name: 'cuisine 2',
        id: 'cui2',
        is_multiselect: true,
        item_list: ['cu1']
      }
    ]
  }
];

const getDesiredList = (masterList, selectedList) =>
  masterList.reduce((a, c) => {
    const selectedItem = selectedList.find(ent => ent.category_id === c.category_id);

    return [
      ...a,
      {
        ...c,
        meta_list: c.meta_list.reduce((a2, c2) => {
          const meta_list_item = selectedItem && selectedItem.meta_list.find(ent => ent.id === c2.id);

          return [
            ...a2,
            {
              ...c2,
              sel_list: meta_list_item ? meta_list_item.item_list : []
            }
          ];
        }, [])
      }
    ];
  }, []);

console.log(JSON.stringify(getDesiredList(exampleMasterList, exampleSelectedList), null, 2));


1
几乎完美,我喜欢它不改变原始数据结构的特点。 这里reduce可以简化为map。 此外,结果不正确,代码应为: sel_list: meta_list_item ? meta_list_item.item_list : [ c2.item_list[c2.item_list.length - 1] ] - Istador

2
我会使用JSON.parse(JSON.stringify(_))来深度克隆两个对象,然后将存在于selectedList但不存在于masterList中的任何属性复制到克隆的masterList对象副本中。
由于你正在处理字符串(因为使用了JSON.stringify),所以可以使用String#replaceitem_list替换为sel_list
现在最后一个问题是将masterList数组项与相应的selectedList数组项匹配。一种方法是只使用index,但这显然不太安全或可靠。我建议提供一个函数给执行合并的方法,该函数将接受每个数组中的一个项目,并确定它们是否equal。此函数还必须能够理解您所引用的是哪个数组(即masterListmeta_listitem_list...)。
以下是一个相当抽象的解决方案,唯一定制的部分是末尾的最后5行,将item_list替换为sel_list并管理数组匹配,其他所有内容都是可重复使用的。(我已经将两个对象转换为字符串以节省垂直空间,您可以专注于解决方案,而不是向下滚动)。

let masterList = `[{"category_id":1,"meta_list":[{"name":"Cuisine","id":"QWEQWEQWE","is_multiselect":false,"item_list":["Arabian","Indian"]},{"name":"Cost for Two","id":"SFDFSDASDASDASD","is_multiselect":true,"item_list":["AED 0 - 100","AED 100 - 200","Greater Than AED 200"]}]},{"category_id":2,"meta_list":[{"name":"Cat 2","id":"cat2","is_multiselect":false,"item_list":["cat 2 1","cat 2 2"]},{"name":"cuisine 2","id":"cui2","is_multiselect":true,"item_list":["cu1","cu2"]}]}]`;
let selectedList = `[{"category_id":1,"meta_list":[{"name":"Cuisine","id":"QWEQWEQWE","is_multiselect":false,"item_list":["Arabian"]},{"name":"Cost for Two","id":"SFDFSDASDASDASD","is_multiselect":true,"item_list":["AED 100 - 200","Greater Than AED 200"]}]},{"category_id":2,"meta_list":[{"name":"cuisine 2","id":"cui2","is_multiselect":true,"item_list":["cu1"]}]}]`;

/**
 * Deep merges 2 objects (if a property exists for both objects then only the original will be used)
 * @param {{} | string} original Original object in object or json format
 * @param {{} | string} other Other object in object or json format
 * @param {(original: string | {}, other: string | {}) => boolean} manageList Function called to compare items in the arrays
 * @returns {{}} 
 */
function mergeJSONs(original, other, manageList) {
  original = typeof original === "string" ? original : JSON.stringify(original); // Deep clone both objects
  other = typeof other === "string" ? other : JSON.stringify(other);
  return recursiveMerge(JSON.parse(original), JSON.parse(other), manageList);
}

/**
 * Copies all the properties that exist in the `other` object to the `original` object
 * @param {{}} original Object to copy the properties to
 * @param {{}} other Object to copy the properties from
 * @param {(original: string | {}, other: string | {}) => boolean} manageList Function called to compare items in the arrays
 * @returns {{}}
 */
function recursiveMerge(original, other, manageList) {
  if (original instanceof Array) {
    if (!(other instanceof Array)) throw Error("Incompatible Types");
    var matchedList = [];
    for (var otherItem of other) {
      var originalIndex = original.findIndex(function(originalItem, index) {
        if (manageList.call(this, originalItem, otherItem) && !matchedList.includes(index)) return matchedList.push(index);
      });
      if (originalIndex === -1) original.push(otherItem);
      else if (typeof otherItem === "object") recursiveMerge(original[originalIndex], otherItem, manageList);
    }
  } else {
    if (other instanceof Array) throw Error("Incompatible Types");
    for (var key in other) {
      if (!(key in original)) original[key] = other[key];
      else if (typeof other[key] === "object") recursiveMerge(original[key], other[key], manageList);
    }
  }
  return original;
}

console.log(mergeJSONs(masterList, selectedList.replace(/"item_list":/g, "\"sel_list\":"), function(original, other) {
  if (typeof original === "string" && typeof other === "string") return original === other;
  else if (typeof original === "object" && typeof other === "object") {
    if ("category_id" in original && "category_id" in other) return original.category_id === other.category_id;
    if ("id" in original && "id" in other) return original.id === other.id;
  }
}));


2

受Mateusz Siniarski启发,生成所需的输出并将reduce替换为map

转化为通俗易懂的表述:通过Mateusz Siniarski的灵感,在生成期望的结果时,把reduce替换成了map。
const derivedList = masterList.map(master => {
  const selected = selectedList.find(el => el.category_id === master.category_id)
  return {
    ...master,
    meta_list: master.meta_list.map(meta => {
      const { item_list } = selected.meta_list.find(el => el.id === meta.id) || {}
      return {
        ...meta,
        sel_list: item_list || [ meta.item_list[meta.item_list.length - 1] ]
      }
    })
  }
})

masterList 进行变异的迭代版本:

masterList.forEach(master => {
  const selected = selectedList.find(el => el.category_id === master.category_id)
  master.meta_list.forEach(meta => {
    const { item_list } = selected.meta_list.find(el => el.id === meta.id) || {}
    meta.sel_list = item_list || [ meta.item_list[meta.item_list.length - 1] ]
  })
})

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