0

I'm new to javascript, so this question may sound very basic.

var data = [
 { Amy: {items:[{shirt: 12},{trouser: 10}] } },
 { Bill: {items:[{shirt: 10},{trouser: 11}] } },
 { Chet: {items:[{shirt: 11},{trouser: 12}] } }
];

I am trying to write a function to return who's got max number of shirts. So I write two functions like this to first get the max values

    var shirtValues = data.map(function(obj) {return obj.items[shirts]; });
    var maxValue = Math.max.apply(null, shirtValues);
    console.log(maxValue);

Now i need to find who is the person that got max number of shirts. How to achieve that?

anoop chandran
  • 1,460
  • 5
  • 23
  • 42
  • 1
    and with two or more items with max? which one would you like to get? – Nina Scholz Oct 12 '16 at 06:45
  • if you are comfortable to use a library like underscore.js lib, you can easily get whatever you need from given objects or arrays. – KnowGe Oct 12 '16 at 06:51
  • @Nina Scolz: Any one. shirt or trousers, or both. – anoop chandran Oct 12 '16 at 06:53
  • @KnowGe: Okay, I will check underscore, never used it before.. Is there other helper libraries similar to underscore? – anoop chandran Oct 12 '16 at 06:55
  • This has been answered before, what you should do is sorting the array/ objects by the desired property and then get the first item of this array. See here: http://stackoverflow.com/questions/5073799/how-to-sort-a-javascript-array-of-objects-by-nested-object-property – axel.michel Oct 12 '16 at 07:02
  • @anoopchandran Of course, there are some alternatives, for example SugarJs (http://sugarjs.com) is one of them, there is also Lodash (https://lodash.com).. – KnowGe Oct 12 '16 at 07:03

6 Answers6

2

I would start by changing your data structure. It's hard to see how the current format would be overly useful for iterating, aggregating, getting the user's name, getting the item's names, etc., without excessive iterating.

Here is one alternative data structure that is much easier to work with because it doesn't have nested arrays and doesn't require Object.keys to access data you need consistently (e.g. user name):

var data = [{
  user: 'Amy',
  items: {
    shirt: 12,
    trouser: 10
  }
}, {
  user: 'Bill',
  items: {
    shirt: 10,
    trouser: 11
  }
}, {
  user: 'Chet',
  items: {
    shirt: 11,
    trouser: 12
  }
}];

With this format, you could easily sort by a particular item quantity:

let getTopItem = (data, itemName) => {
  // 1. clone the array using concat
  // 2. sort by value at supplied itemName
  // 3. return the first item
  let sorted = data.concat().sort((a, b) => {
    return b.items[itemName] - a.items[itemName];
  });
  return sorted.shift();
}

let topShirts = getTopItem(data, 'shirt');
console.log(topShirts.user);

EDIT - I don't mean this negatively toward any of the answers as they all seem to be correct and useful approaches to getting the required data from the presented data structure - but look at how many iterations they all require to get this very basic data out of your object. Choosing the right structure for your data will save you a lot of headaches down the road.

Rob M.
  • 35,491
  • 6
  • 51
  • 50
1

Provided that, you cannot change your datastructure, reduce function can be very handy to serve your purpose. Actually, the logic becomes pretty straight-forward!. The code is provided below:

    var data = [
     { Amy: {items:[{shirt: 12},{trouser: 10}] } },
     { Bill: {items:[{shirt: 10},{trouser: 11}] } },
     { Chet: {items:[{shirt: 11},{trouser: 12}] } }
    ];

    var new_data = data.reduce(function(max, obj) {
     var obj_val;
     var max_val;
 
     max_val = get_shirt_val(max);
     obj_val = get_shirt_val(obj);
  
     return obj_val > max_val ? obj : max;
    });

    function get_shirt_val(obj) {
        key = Object.keys(obj)[0]
     return obj[key].items[0].shirt;
    }

    console.log(JSON.stringify(new_data));
    console.log(Object.keys(new_data)[0])

Hope this helps!

Sazzadur Rahaman
  • 6,938
  • 1
  • 30
  • 52
0

If your JSON structure is this always. Then you can use this way to find maximum shirts count :

var max=0;
var maxShirtCount=null;
data.forEach(function(ele,ind){ 
  currVal = ele[Object.keys(ele)[0]].items[0].shirt;
  if(currVal>max){
    max=currVal;
    maxShirtCount=ele;
  }
});
console.log(maxShirtCount);

for maximum trousers count :

var max=0;
var maxTrouserCount=null;
data.forEach(function(ele,ind){ 
  currVal = ele[Object.keys(ele)[0]].items[1].trouser;
  if(currVal>max){
    max=currVal;
    maxTrouserCount=ele;
  }
});
console.log(maxTrouserCount);
AB Udhay
  • 643
  • 4
  • 9
0

You could get the key, as name, get for the wanted item the count and make a comparison with the count of the previous persons. If max is the same, then extend the result array, if greater, then return a new object with the max data.

function getMax(item) {
    return data.reduce(function (r, a) {
        var name = Object.keys(a)[0],
            count = a[name].items.reduce(function (s, b) {
                return s + (b[item] || 0);
            }, 0);

        if (count >= r.count) {
            if (count > r.count) {
                return { item: item, names: [name], count: count };
            }
            r.names.push(name);
        }
        return r;
    }, { item: item, names: [], count: 0 });
}

var data = [{ Amy: { items: [{ shirt: 12 }, { trouser: 10 }] } }, { Bill: { items: [{ shirt: 10 }, { trouser: 11 }] } }, { Chet: { items: [{ shirt: 11 }, { trouser: 12 }] } }],
    maxShirt = getMax('shirt');

console.log(maxShirt);       // all names with max count
console.log(maxShirt.names); // just the names
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
0
var data = [
 { Amy: {items:[{shirt: 12},{trouser: 10}]  } },
 { Bill: {items:[{shirt: 10},{trouser: 11}] } },
 { Chet: {items:[{shirt: 11},{trouser: 12}] } }
];

function doSort(a, b){
  return a[Object.keys(a)].items[0].shirt < b[Object.keys(b)].items[0].shirt;
}

console.log(Object.keys(data.sort(doSort)[0])[0]);
vadimk
  • 1,461
  • 15
  • 11
0

Without sorting and with a single pass reduce you might do as follows;

var data = [{ Amy: {items:[{shirt: 12},{trouser: 10}] } }, { Bill: {items:[{shirt: 10},{trouser: 11}] } }, { Chet: {items:[{shirt: 11},{trouser: 12}] } }
], 
  result = data.reduce((p,c) => { var sc = c[Object.keys(c)[0]].items[0].shirt;
                                  return sc > p[0] ? [sc,c] : p;
                                },[0,{}])[1];
console.log(result);
Redu
  • 25,060
  • 6
  • 56
  • 76