简体   繁体   中英

Vanilla Javascript merge JSON array objects into nested array if match a common value

I was unable to find a solution that is identical with the issue I am trying to solve. If is a duplicate kindly provide a link to a solution.

Using vanilla Javascript, merge array of objects that has a common value. I have a JSON file and it will create the following javascript object (I am unable to change the original structure). Notice that each object will have different nested name and nested value. However the common value is the name[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'
  }
]

The desire output will be merging the nested object that has the same name[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'
  }
]

Using this new merged structure, I would create a function to loop each array set. Thanks in advance

You could key the data by the first name entry using a Map. Then populate the data into the value property within the corresponding Map value, also collect all the extra values in the name arrays (beyond the first two entries), and finally extract the Map values.

This works with linear time complexity:

 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); 

The callback function that is passed to Array.from is a map-function. It is only needed to turn value arrays with only one string into that string only. If this is not required, you can omit that callback and just do Array.from(map.values()) .

With some Array methods, I think you can make this work (untested code below, but I think it should do what you want):

// 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];
    });
});

You can also use reduce and map to create your desired outcome. I think it is quite powerful combination in general.

    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;
    })

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM