0

I am trying to create a new array of object from an existing array of object based on the common attribute (category.blocktitle) value inside the object. My array of object looks like this.

[
  {
    "category": {
      "name": "test1111",
      "val": "test111111111",
      "blocktitle": ".test.0"
    }
  },
  {
    "category": {
      "name": "test22",
      "val": "test2222",
      "blocktitle": ".test.0.test2"
    }
  },
  {
    "category": {
      "name": "test1111111",
      "val": "test11111111111",
      "blocktitle": ".test.0"
    }
  },
  {
    "category": {
      "name": "test2222",
      "val": "test222222",
      "blocktitle": ".test.0.test2"
    }
  }
]

My desired output is

[
  {
    "category": {
       "name_val" : [
              {
                     "name": "test1111",
                     "val": "test111111111"
              }, 
              { 
                     "name": "test1111111",
                     "val": "test11111111111",
              }
       ]
    },
    "blocktitle": ".test.0"
  },
  {
    "category": {
        "name_val" : [
                  {
                         "name": "test22",
                         "val": "test2222"
                  }, 
                  { 
                         "name": "test2222",
                         "val": "test222222",
                  }
        ]
    },
    "blocktitle": ".test.0.test2"
  }
]

I have tried reduce / map / filter but not sure what I am doing wrong.

2
  • Just so it is explicitly stated, how are you wanting to "match"? Because the names that you have grouped are not equal. Or is it that you are matching on the blocktitle? Commented Nov 2, 2017 at 15:13
  • 4
    “I have tried reduce / map / filter” — Great! Show your attempt in your question! We also won’t know what you’re doing wrong, unless you include your attempt and a clear problem statement. Commented Nov 2, 2017 at 15:13

5 Answers 5

3

I would use ES6 Array.prototype.reduce and Array.prototype.filter methods:

let result = data.reduce((acc, d) => { 
  const found = acc.find(a => a.blocktitle === d.category.blocktitle);
  const value = { name: d.category.name, val: d.category.val };
  if(found) {
    found.category.name_val.push(value);
  }
  else {
    acc.push({blocktitle: d.category.blocktitle, category: { name_val: [value]} });
  }
  return acc;
}, []);

Where data is your initial data array.

Sign up to request clarification or add additional context in comments.

7 Comments

This answer is not performant because you are making a search for all elements in the array with the code acc.find(a => a.blocktitle === d.category.blocktitle)..
@YosvelQuintero Maybe your a.findIndex(e => e.blocktitle === blocktitle) is more performant? ;)
he findIndex() method returns the index of the first element in the array that satisfies the provided testing function. Otherwise -1 is returned. - But, notice that I only do Array.prototype.findIndex() if objHash[blocktitle] evaluate falsey
@YosvelQuintero Yes, and the same is true also for find. Per MDN: The find() method returns the value of the first element in the array that satisfies the provided testing function. Otherwise undefined is returned.
But you are doing Array.prototype.find() for every elements in the Array.prototype.reduce() - And that is not performant ;)
|
3

var test = [
  {
    "category": {
      "name": "test1111",
      "val": "test111111111",
      "blocktitle": ".test.0"
    }
  },
  {
    "category": {
      "name": "test22",
      "val": "test2222",
      "blocktitle": ".test.0.test2"
    }
  },
  {
    "category": {
      "name": "test1111111",
      "val": "test11111111111",
      "blocktitle": ".test.0"
    }
  },
  {
    "category": {
      "name": "test2222",
      "val": "test222222",
      "blocktitle": ".test.0.test2"
    }
  }
];

//create a map of blocktitle to elements with that blocktitle
var temp = test.reduce(function(collection, element){
  if (collection[element.category.blocktitle] === undefined) collection[element.category.blocktitle] = [];
  
  collection[element.category.blocktitle].push(element);
  return collection;
}, {});

//convert temp to expected result
var result = Object.keys(temp).map(function(blocktitle){
  return {
    blocktitle: blocktitle,
    category: {
      name_val: temp[blocktitle].map(function(element){
        return { name: element.category.name, val: element.category.val };
      })
    }
  };
});

console.log(result);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Comments

1

You could use an object as reference to the same group.

var data = [{ category: { name: "test1111", val: "test111111111", blocktitle: ".test.0" } }, { category: { name: "test22", val: "test2222", blocktitle: ".test.0.test2" } }, { category: { name: "test1111111", val: "test11111111111", blocktitle: ".test.0" } }, { category: { name: "test2222", val: "test222222", blocktitle: ".test.0.test2" } }],
    hash = Object.create(null),
    grouped = [];

data.forEach(function (o) {
    var key = o.category.blocktitle;
    if (!hash[key]) {
        hash[key] = [];
        grouped.push({ category: { name_val: hash[key], blocktitle: o.category.blocktitle } });
    }
    hash[key].push({ name: o.category.name, val: o.category.val });
});

console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

1

You can use Array.prototype.reduce():

const data = [{category: {name: 'test1111',val: 'test111111111',blocktitle: '.test.0'}},{category: { name: 'test22', val: 'test2222', blocktitle: '.test.0.test2' }},{category: {name: 'test1111111',val: 'test11111111111',blocktitle: '.test.0'}},{category: {name: 'test2222',val: 'test222222',blocktitle: '.test.0.test2'}}]
const result = data.reduce((a, c) => {
  let blocktitle = c.category.blocktitle;
  let objNameVal = { name: c.category.name, val: c.category.val };

  if (a.hash[blocktitle]) {
    let index = a.array.findIndex(e => e.blocktitle === blocktitle);
    a.array[index].category.name_val.push(objNameVal);
  } else {
    a.hash[blocktitle] = true;
    a.array.push({ category: { name_val: [objNameVal] }, blocktitle: blocktitle });
  }
  return a;
}, { array: [], hash: {} });

// Clean hash..
delete result.hash;

console.log(result.array);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Comments

0

You can use a reduce to index the items by blocktitle, and then map that object to an array.

function convert(input) {
  const byBlocktitle = input.reduce((byBlocktitleOut, inputObject) => {
    const blocktitle = inputObject.category.blocktitle;

    byBlocktitleOut[blocktitle] = byBlocktitleOut[blocktitle] || [];
    byBlocktitleOut[blocktitle].push(inputObject);

    return byBlocktitleOut;
  }, {});

  return Object.keys(byBlocktitle).map((blocktitle) => {
    const inputObjects = byBlocktitle[blocktitle];

    return {
      category: {
        name_val: inputObjects.map((inputObject) => {
          return {
            name: inputObject.category.name,
            val: inputObject.category.val,
          };
        }),
      },
      blocktitle: blocktitle,
    };
  });
}

Working example/test: http://jsbin.com/yifapacupi/edit?js,console,output

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.