Home > OS >  Merge array of objects with multiple same keys (Dynamic keys)
Merge array of objects with multiple same keys (Dynamic keys)

Time:11-09

What is the best way to merge array contents from JavaScript objects sharing multiple key in common and keys list is also dynamic so we don't know that how much keys are there key is there in keyname with prefix?

How can array in the example below be reorganized into output?


var keys = [{"id":"ABC","name":"abc"},{"id":"DEF","name":"def"},{"id":"GHI","name":"ghi"}]

var array = [
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East",
    "device": "Samsung"
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": ["MI", "Oppo"]
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East"
    "device": "Apple"
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": "Blackberry"
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Tab",
    "input_DEF-operator": "==",
    "input_DEF-expression": "North",
    "device": "One Plus"
  }
]

Expected Output:

Var output = [
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East",
    "device": ["Samsung", "Apple"]
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": ["MI", "Oppo", "Blackberry"]
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Tab",
    "input_DEF-operator": "==",
    "input_DEF-expression": "North",
    "device": ["One Plus"]
  }
]

CodePudding user response:

One way to do it is just to use regular loops and statements. Also it seems from your question, that you need to compare all keys of objects except device key. Check inline comments:

// Array of objects
const array = [
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East",
    "device": "Samsung"
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": ["MI", "Oppo"]
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East",
    "device": "Apple"
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": "Blackberry"
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Tab",
    "input_DEF-operator": "==",
    "input_DEF-expression": "North",
    "device": "One Plus"
  }
];

// Create result array.
const result = [];

// For each object in array
for(const obj of array) {
  // Create trigger for new array elements, that have unique key values
  let trigger_new = true;
  // Compate current object keys to objects keys in result array
  // and if all keys is the same, push current device to result
  for(const resObj of result) {
    // Another trigger to control non equals keys values
    let trigger_not_equals_keys = true;
    // Here we implement compare logic.
    // Basically we need to compare all object keys except device
    for(const resKey in resObj) for(const key in obj) {
      // First check that both objects has same set of keys
      // If not, set trigger_not_equals_keys to false
      if(!resObj[key] || !obj[resKey]) trigger_not_equals_keys = false;
      // Here we check that key is not device, keys are the same
      // and if their values differs, set trigger_not_equals_keys to false
      if(
        resKey !== "device" && key !== "device" && // Check if not device key
        resKey === key &&                          // Check that keys is the same
        resObj[resKey] !== obj[key]                // Check if values of same keys differs
      ) trigger_not_equals_keys = false;
    }
    // If trigger_not_equals_keys still true, then all keys values
    // except device is the same and we can merge devices array
    if(trigger_not_equals_keys) {
      // Set trigger_new to false, because we found same set of keys->values
      // in result array of objects
      trigger_new = false;
      // Check if device value is array or not
      if(Array.isArray(resObj.device)) resObj.device.push(obj.device);
      else resObj.device = [resObj.device, obj.device];
    }
  }
  // If trigger_new still true, push obj to result as it has unique keys values
  if(trigger_new) result.push(obj);
}

// Test
console.log(result);

CodePudding user response:

My approach would be to use the keys other than device as a unique key and use JSON.stringify to make it as a key, then map them based on those unique keys, then finally remap them into a new array:

var array = [{
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East",
    "device": "Samsung",
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": ["MI", "Oppo"],
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Web",
    "input_DEF-operator": "==",
    "input_DEF-expression": "East",
    "device": "Apple",
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Mobile",
    "input_DEF-operator": "==",
    "input_DEF-expression": "West",
    "device": "Blackberry",
  },
  {
    "input_ABC-operator": "==",
    "input_ABC-expression": "Tab",
    "input_DEF-operator": "==",
    "input_DEF-expression": "North",
    "device": "One Plus",
  },
];

const output = Object.entries(array.reduce((acc, obj) => {
  const {
    device,
    ...curr
  } = obj
  return ({
    ...acc,
    [JSON.stringify(curr)]: acc[JSON.stringify(curr)] ? [...acc[JSON.stringify(curr)], ...(typeof(obj.device) === "string" ? [obj.device] : obj.device)] : (typeof(obj.device) === "string" ? [obj.device] : obj.device)
  })
}, {})).map(([k, v]) => ({
  ...JSON.parse(k),
  device: v
}))

console.log(output);

  • Related