Search code examples
javascriptreactjsarraysjsonpatharray-merge

Merge array of objects with multiple same keys (Dynamic keys)


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"]
  }
]

Solution

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