Farid
Farid

Reputation: 1665

Rearrange an array of objects based on a common key/value pair

I have the following array of objects:

[
   {
     message: 'This is a test',
     from_user_id: 123,
     to_user_id: 567
   },
   {
     message: 'Another test.',
     from_user_id: 123,
     to_user_id: 567
   },
   {
     message: 'A third test.',
     from_user_id: '456',
     to_user_id: 567
   }
]

How do I construct a new array of objects where the outermost object key is based on a common key found in the original array?

This is what I'm after:

[
  {
    123: [
      {
        message: 'This is a test',
        from_user_id: 123,
        to_user_id: 567
      },
      {
        message: 'Another test.',
        from_user_id: 123,
        to_user_id: 567
      }
    ]
  },
  {
    456: [
      {
        message: 'A third test.',
        from_user_id: '456',
        to_user_id: 567
      }
    ]
  }
]

Notice how in the first array, the user ID of 123 shows up in two objects. That would be the object key for the first element in the new array.

Upvotes: 2

Views: 2435

Answers (4)

SeleM
SeleM

Reputation: 9658

var users = [
             {
               message: 'This is a test',
               from_user_id: 123,
               to_user_id: 567
             },
             {
               message: 'Another test.',
               from_user_id: 123,
               to_user_id: 567
             },
             {
               message: 'A third test.',
               from_user_id: '456',
               to_user_id: 567
             }
          ];
console.log(_.groupBy(users,'from_user_id'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.min.js"></script>

Using Lodash will make it super easy for you. Assuming you have :

var users = [
             {
               message: 'This is a test',
               from_user_id: 123,
               to_user_id: 567
             },
             {
               message: 'Another test.',
               from_user_id: 123,
               to_user_id: 567
             },
             {
               message: 'A third test.',
               from_user_id: '456',
               to_user_id: 567
             }
          ];

just in one line with Lodash and you'll get want you want exactly:

users = _.groupBy(users,'from_user_id')

Upvotes: 1

Nina Scholz
Nina Scholz

Reputation: 386736

You could use an object and take the from_user_id property as key for the object. Then push the actual object to the group. For getting the final result, iterate the keys of groups and build a new object for any group.

var data = [{ message: 'This is a test', from_user_id: 123, to_user_id: 567 }, { message: 'Another test.', from_user_id: 123, to_user_id: 567 }, { message: 'A third test.', from_user_id: '456', to_user_id: 567 }],
    groups = Object.create(null),
    result;

data.forEach(function (a) {
    groups[a.from_user_id] = groups[a.from_user_id] || [];
    groups[a.from_user_id].push(a);    
});

result = Object.keys(groups).map(function (k) {
    var temp = {};
    temp[k] = groups[k];
    return temp;
});

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

Together with a single loop approach

var data = [{ message: 'This is a test', from_user_id: 123, to_user_id: 567 }, { message: 'Another test.', from_user_id: 123, to_user_id: 567 }, { message: 'A third test.', from_user_id: '456', to_user_id: 567 }],
    result = data.reduce(function (groups) {
        return function (r, a) {
            var temp = {};
            if (!groups[a.from_user_id]) {
                groups[a.from_user_id] = [];
                temp[a.from_user_id] = groups[a.from_user_id]; 
                r.push(temp);
            }
            groups[a.from_user_id].push(a);
            return r;
        };
    }(Object.create(null)), []);

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

Upvotes: 5

cнŝdk
cнŝdk

Reputation: 32145

You just need to initialize a results array, loop over your data array and check if the iterated from_user_id exists in the results array, push the iterated object on it, otherwise create a new object with the new from_user_id key.

This is how should be your code:

var results = [];
arr.forEach(function(obj){
    let id = obj["from_user_id"];
    if(!results.some(function(r){
       return r[id];
    })){
        let el = {}; 
        el[id] = [];
        el[id].push(obj);
        results.push(el);
    }else{
        results.forEach(function(res){
            if(res[id]){
              res[id].push(obj);
            }
        });
    }
});

Demo:

var arr = [{
  message: 'This is a test',
  from_user_id: 123,
  to_user_id: 567
}, {
  message: 'Another test.',
  from_user_id: 123,
  to_user_id: 567
}, {
  message: 'A third test.',
  from_user_id: 456,
  to_user_id: 567
}];


var results = [];
arr.forEach(function(obj){
    let id = obj["from_user_id"];
    if(!results.some(function(r){
       return r[id];
    })){
        let el = {}; 
        el[id] = [];
        el[id].push(obj);
        results.push(el);
    }else{
        results.forEach(function(res){
            if(res[id]){
              res[id].push(obj);
            }
        });
    }
});
console.log(results);

Upvotes: 1

Hamuel
Hamuel

Reputation: 633

You could go full functional get all the key filter it out and map it back as a json object

var b = a.map(key => key['from_user_id'])
var c = {}
b.map(elt => c[elt] = a.filter(k => k.from_user_id == elt))
console.log(c)

Upvotes: 4

Related Questions