1

I want to modify a Javascript array, so that the elements having the same values for specifies properties merge into one object in a way that the other properties are kept as a comma-separated string, JSON string, or an array. Basically, I want to turn this:

[
    {
        "language" : "english",
        "type" : "a",
        "value" : "value1"
    },
    {
        "language" : "english",
        "type" : "a",
        "value" : "value2"
    },
    {
        "language" : "english",
        "type" : "b",
        "value" : "value3"
    },  
    {
        "language" : "spanish",
        "type" : "a",
        "value" : "valor1"
    }
]

into this:

[
    {
        "language" : "english",
        "type" : "a",
        "value" : ["value1" , "value2"]  // A Json string is welcome too
    },
    {
        "language" : "english",
        "type" : "b",
        "value" : "value3"
    },  
    {
        "language" : "spanish",
        "type" : "a",
        "value" : "valor1"
    }
]

I have tried iterating and filtering, then upserted the object as given in the snippet. But I wonder if there is a more elegant way to do that.

P.S EcmaScript6 and additional JS library suggestions are also welcome.

var originalArray = [
 {
  "language" : "english",
  "type" : "a",
  "value" : "value1"
 },
 {
  "language" : "english",
  "type" : "a",
  "value" : "value2"
 },
 {
  "language" : "english",
  "type" : "b",
  "value" : "value3"
 }, 
 {
  "language" : "spanish",
  "type" : "a",
  "value" : "valor1"
 }
];

var resultingArray = [];

// iterate through the original array
$.each(originalArray, function(i, val) {
  // apply filter on key properties (i.e. language and type)
  var result = resultingArray.filter(function( obj ) {
    return (obj.language === val.language && obj.type === val.type);
  });
  // if a record exists, update its value
  if (result.length === 1) {  
    result[0].value += (", " + val.value);
  } 
  // else, add value
  else if (result.length === 0) {
    resultingArray.push(val);
  }
  // if multiple rows exist with same key property values...
  else {
    alert("Too many records with language '" + val.language + "' and type '" + val.type + "'");
  }
});

console.log(JSON.stringify(resultingArray));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
vahdet
  • 6,357
  • 9
  • 51
  • 106
  • 1
    [Here was my suggested solution](http://pastebin.com/bx66Vc5K), depending on what you consider "elegant". The trouble with your code is it runs in `O(N^2)`, since the `filter` function runs once on the first iteration of `each`, twice, on the second iteration, and so on. There's no need for that. – A. Vidor Jul 27 '16 at 09:57
  • 1
    And [here](https://jsfiddle.net/Arnauld/0eyxbf70/) was mine, where `value` is always an array. BTW: I think your `alert()` statement can never be triggered. If you don't want twice the same _value_ for the same key (language + type), you'll have to do it differently. – Arnauld Jul 27 '16 at 10:04
  • hey, your JS was using jQuery, why you don't add jQuery tag? I was learn and make sure using pure javascript `forEach` to help you -_-! – Fadhly Permata Jul 27 '16 at 10:12
  • 1
    Incidentally, I'm not sure it can be seen an _exact_ duplicate of [the suggested question](http://stackoverflow.com/questions/31688459/group-array-items-using-object) as the OP is using a _composite key_, making the existing answers inadequate. – Arnauld Jul 27 '16 at 10:18
  • @Arnauld I'm not sure adding a secondary check warrants an entirely new question. The answers given are entirely appropriate, they just require a small modification. – CodingIntrigue Jul 27 '16 at 11:07
  • By elegant, I meant some kind of collections or map implementation indeed. Like, um, not an explicit loop. – vahdet Jul 27 '16 at 11:19
  • Up vote since I think this question shows effort. Stop the negativity dudes. – span Jun 19 '19 at 07:04

1 Answers1

1

This is what you need?

var baseData= [
    {
        "language"  : "english",
        "type" : "a",
        "value" : "value1"
    },
    {
        "language" : "english",
        "type" : "a",
        "value" : "value2"
    },
    {
        "language" : "english",
        "type" : "b",
        "value" : "value3"
    },  
    {
        "language" : "spanish",
        "type" : "a",
        "value" : "valor1"
    }
];

var newData = [];
baseData.forEach(function(item, index) {
  if (newData.length === 0) {
    newData.push(item);
    
  } else {
    var dIndex = -1;
    newData.forEach(function(itm, idx) {
      if (item.language === itm.language && item.type === itm.type) dIndex = idx;
    });
    
    if (dIndex !== -1) {
      var oldValue = newData[dIndex].value;
      
      if (typeof(oldValue).toString() === 'string') {
        newData[dIndex].value = [oldValue, item.value];
      }
    } else {
      newData.push(item);
    }
  }
});
console.log(newData);
Fadhly Permata
  • 1,686
  • 13
  • 26