【问题标题】:Vanilla Javascript merge JSON array objects into nested array if match a common value如果匹配一个公共值,Vanilla Javascript 将 JSON 数组对象合并到嵌套数组中
【发布时间】:2019-08-13 13:48:59
【问题描述】:

我无法找到与我要解决的问题相同的解决方案。如果是重复的,请提供解决方案的链接。

使用 vanilla Javascript,合并具有共同值的对象数组。我有一个 JSON 文件,它将创建以下 javascript 对象(我无法更改原始结构)。请注意,每个对象都有不同的嵌套名称和嵌套值。然而共同的价值是名字[0]

var data = [
  {
    name: [
      'Data 1', // common value
      '1 Jan, 2019', // same value therefore not overwrite/merge
      'hotfix dec'
    ],
    value: [
      'hotfix1.fsfix',
      'hotfix1.txt'
    ]
  },
  {
    name: [
      'Data 1', // common value
      '1 Jan, 2019' // same value therefore not overwrite/merge
    ],
    value: 'data1.jar'
  },
  {
    name: [
      'Data 2',
      '1 Feb, 2019'
    ],
    value: 'data2.fsfix'
  },
  {
    name: [
      'Data 2',
      '1 Feb, 2019'
    ],
    value: 'data2.jar'
  },
  {
    name: [
      'Data 3',
      '1 Mar, 2018'
    ],
    value: 'data3.fsfix'
  }
]

愿望输出将合并具有相同名称[0]的嵌套对象。

var data = [
  {
    name: [
      'Data 1', // common value
      '1 Jan, 2019', // same value therefore not overwrite/merge
      'hotfix dec'
    ],
    value: [
      'data1.fsfix',
      'data1.txt',
      'data1.jar' // This was added after the merge
    ]
  },
  {
    name: [
      'Data 2',
      '1 Feb, 2019'
    ],
    value: [
      'data2.fsfix',
      'data2.jar' // This was added after the merge
    ]
  },
  {
    name: [
      'Data 3',
      '1 Mar, 2018'
    ],
    value: 'data3.fsfix'
  }
]

使用这个新的合并结构,我将创建一个函数来循环每个数组集。提前致谢

【问题讨论】:

  • 你自己试过了吗?我会用 array.reduce 来做到这一点
  • 你能提供你已经做过的代码吗?
  • 即使您可以按照 Chris Li 的建议使用 array.reduce 来执行此操作,但通常将数组减少为单个值。对于您的问题,我建议使用 array.map

标签: javascript arrays json object


【解决方案1】:

您可以使用 Map 通过名字条目来键入数据。然后将数据填充到对应 Map 值内的value 属性中,同时收集name 数组中的所有额外值(前两个条目之外),最后提取 Map 值。

这适用于线性时间复杂度:

var data = [{name: ['Data 1', '1 Jan, 2019', 'hotfix dec'],value: ['hotfix1.fsfix','hotfix1.txt']},{name: ['Data 1', '1 Jan, 2019'],value: 'data1.jar'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.fsfix'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.jar'},{name: ['Data 3','1 Mar, 2018'],value: 'data3.fsfix'}];

const map = new Map(data.map(o => 
    [o.name[0], { name: o.name.slice(0,2), value: [] }]));
data.forEach(o => map.get(o.name[0]).value.push(...[].concat(o.value)));
data.forEach(o => map.get(o.name[0]).name.push(...o.name.slice(2)));
const result = Array.from(map.values(), o => o.value.length === 1 
    ? { ...o, value: o.value[0] } : o);

console.log(result);

传递给Array.from 的回调函数是一个映射函数。只需将只有一个字符串的值数组转换为该字符串即可。如果这不是必需的,您可以省略该回调,只需执行Array.from(map.values())

【讨论】:

  • 感谢您的详细解释。结果足够接近,但仅由于 concat(o.value) 而缺少名称 [2] -> hotfix dec。抱歉这个愚蠢的问题,但什么是 ...o 和 ...[] ?
  • 我更新了我的答案以包含缺少的name[2]o 是函数参数(只是我选择的名字);它是一个变量,对应于数据数组中的一个对象。 [] 是一个空数组。 [].concat 使用传递给 concat 的参数扩展该空数组,该参数可以是简单值或数组。
  • 非常感谢。完美运行。我的意思是 [] 和 o 之前的 3 个点 ... 的术语/功能是什么?试图用谷歌搜索,但没有找到任何东西。
  • 是展开语法。当[].concat() 返回一个数组时,... 会将数组元素作为单独的函数参数“传播”。与 o.name.slice(2) 相同(也是一个数组)。
  • 酷...我从你身上学到了很多!谢谢一堆。非常感谢
【解决方案2】:

您还可以使用reducemap 来创建您想要的结果。总的来说,我认为这是一个相当强大的组合。

    const data = [{name: ['Data 1', '1 Jan, 2019', 'hotfix dec'],value: ['hotfix1.fsfix','hotfix1.txt']},{name: ['Data 1', '1 Jan, 2019'],value: 'data1.jar'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.fsfix'},{name: ['Data 2','1 Feb, 2019'],value: 'data2.jar'},{name: ['Data 3','1 Mar, 2018'],value: 'data3.fsfix'}];

    const dataMap = data.reduce((acc, curr)=>{
        id = curr.name[0];
        acc[id] = acc[id] || {
            name: [],
            value: []
        };
        const value = Array.isArray(curr.value) ? curr.value : [curr.value]

        acc[id].name = [...acc[id].name, ...curr.name.filter((i)=>acc[id].name.indexOf(i)===-1)]
        acc[id].value = [...acc[id].value, ...value.filter((i)=>acc[id].value.indexOf(i)===-1)]

        return acc
    },{})
    const result = Object.keys(dataMap).map(key=> {
        const d = dataMap[key];
        d.value = d.value.length===1 ? d.value[0] : d.value
        return d;
    })

【讨论】:

  • 非常感谢。像魅力一样工作!
【解决方案3】:

使用一些 Array 方法,我认为您可以完成这项工作(下面的代码未经测试,但我认为它应该可以满足您的需求):

// Merge matching entries by pushing values from matching objects to each other
data = data.map(entry => {
    let matchingObjects = data.filter(match => {
        return match.name[0] === entry.name[0];
    });
    matchingObjects.forEach(match => {
        if (match !== entry) {
            var flatValue = [entry.value].flat();
            entry.value = flatValue.push.apply(flatValue, [match.value].flat());
        }
    });
});

// Remove duplicates by filtering out all but the first entry of each name[0]
data = data.filter((entry, index) => {
    return index === data.findIndex(match => {
        return match.name[0] === entry.name[0];
    });
});

【讨论】:

  • 感谢您的建议。正在检查jsfiddle.net/a0e29xjz,但它返回 Uncaught TypeError: Cannot read property 'name' of undefined (line 6)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-11-12
  • 2020-02-21
  • 1970-01-01
  • 1970-01-01
  • 2022-10-01
  • 2021-07-19
  • 1970-01-01
相关资源
最近更新 更多