javascriptarraysarray-merge

Merge objects inside an array of objects with duplicates


I have "available" data and want to convert it into "desired".

I tried to merge all objects inside array using this but its not working coz as you can see for timestamp = 10 we have 2 values of sw_version

const available = [
    {
      "timestamp": 10,
      "sw_version": "AA"
    },
    {
      "timestamp": 10,
      "sw_version": "AB"
    },
    {
      "timestamp": 20,
      "sw_version": "QFX-1.2.5 B"
    },
    {
      "timestamp": 10,
      "pressure": 14.75
    },
    {
      "timestamp": 20,
      "pressure": 14.22
    },
    {
      "timestamp": 10,
      "temperature": 15.96
    },
    {
      "timestamp": 20,
      "temperature": 38.50
    },
{
      "timestamp": 30,
      "temperature": 2.2
    },
    {
      "timestamp": 30,
      "pressure": 9.8
    }
  ]

const desired = [
    {
      "timestamp": 10,
      "sw_version": "AA",
      "pressure": 14.75,
      "temperature": 15.96
    },
    {
      "timestamp": 10,
      "sw_version": "AB",
      "pressure": 14.75,
      "temperature": 15.96
    },
    {
      "timestamp": 20,
      "sw_version": "QFX-1.2.5 B",
      "pressure": 14.22,
      "temperature": 38.5
    },
{
      "timestamp": 30,
      "pressure": 9.8,
      "temperature": 2.2
    }
  ]

const output = available.reduce((result, item) => {
      const i = result.findIndex((resultItem) => resultItem.timestamp === item.timestamp);
      if (i === -1) {
        result.push(item);
      } else {
        result[i] = { ...result[i], ...item };
      }
      return result;
    }, []);
    
console.log(output)

The output was

[
  {
    "timestamp": 10,
    "sw_version": "AB",
    "pressure": 14.75,
    "temperature": 15.96
  },
  {
    "timestamp": 20,
    "sw_version": "QFX-1.2.5 B",
    "pressure": 14.22,
    "temperature": 38.5
  },
  {
    "timestamp": 30,
    "temperature": 2.2,
    "pressure": 9.8
  }
]

As you can see in the desired output i want 2 objects with timestamp = 10 but in the function output its is overwriting the first one and keeping only one object.

We will get multiple values for same timestamp for only one measurement which is sw_version. For other measurements like temperature or pressure we won't get multiple values for same timestamps. sw_version is optional as well so we may get data where sw_version will not be there and we just have to merge without thinking about duplicating rows timestamp is mandatory it will always be there


Solution

  • First, I would group by timestamp and store all sw_version in an array. Then, I would create the expected structure for each sw_version for each timestamp. I would use a special case, if there is no sw_version.

    const available = [{"timestamp": 10,"sw_version": "AA"},{"timestamp": 10,"sw_version": "AB"},{"timestamp": 20,"sw_version": "QFX-1.2.5 B"},{"timestamp": 10,"pressure": 14.75},{"timestamp": 20,"pressure": 14.22},{"timestamp": 10,"temperature": 15.96},{"timestamp": 20,"temperature": 38.50},{"timestamp": 30,"temperature": 2.2},{"timestamp": 30,"pressure": 9.8}];
    
    const output = Object.entries(available.reduce((acc, el) => {
      if (!(el.timestamp in acc)) acc[el.timestamp] = {};
      if ("sw_version" in el) 
        if (!("sw_version" in acc[el.timestamp])) acc[el.timestamp].sw_version = [el.sw_version];
        else acc[el.timestamp].sw_version.push(el.sw_version);
      else
        acc[el.timestamp] = {...acc[el.timestamp], ...el};
      return acc;
    }, {})).flatMap(([timestamp, el]) => !("sw_version" in el)
      ? el
      : el.sw_version.map(sw_version => ({
          ...el,
          sw_version
        }))
    );
    
    console.log(output);