Find parent object with max value - javascript

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?

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.

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!

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

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

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

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

Related

Check if a key value already exists in a multidimensional array object in angularjs

Suppose I have an array with object like this
array = [
{id:2,cnt:2},{id:3,cnt:3},{id:4,cnt:2},
{id:1,cnt:6},{id:2,cnt:7},{id:5,cnt:4},
{id:2,cnt:4},{id:3,cnt:2},{id:4,cnt:2},
{id:3,cnt:2},{id:4,cnt:3},{id:5,cnt:2}
];
where I need to create another array with the object where I need to add cnt value with id.
Output suppose to be like this.
output = [
{id:1,cnt:6},{id:2,cnt:13},{id:3,cnt:7},{id:4,cnt:7},{id:5,cnt:6}
];
what I have tried so far is
var general = [];
angular.forEach(array, function(value){
angular.forEach(value, function(val,key){
angular.forEach(general, function(val1,key1){
if(val1.id === val.id){
val1.cnt +=val.cnt
//#TOD0 how to add value of count and put it on general
}else{
//#TODO
general.push(val);
}
});
});
});
console.log(general);
I am unable to achieve my output. I have marked as TODO where I am confused. Can someone help me? Thanks in advance.
Array.reduce can help you a lot - you basically create a new array, and iterate your current array and check the new array to see if the current item exists. If it does, add the cnt - else add the whole item:
var mashed = arr.reduce(function(m, cur, idx) {
var found = false;
for (var i =0; i < m.length; i++) {
if (m[i].id == cur.id) {
m[i].cnt += cur.cnt;
found = true;
}
}
if (!found) {
m.push(cur)
}
return m;
}, [])
Fiddle demo: https://jsfiddle.net/1ffqv9g0/
var arr = [
{id:2,count:2,color:"red"},{id:3,count:3,color:"black"},{id:4,count:2,color:"white"},
{id:1,count:6,color:"green"},{id:2,count:7,color:"red"},{id:5,count:4,color:"blue"},
{id:2,count:4,color:"red"},{id:3,count:2,color:"black"},{id:4,count:2,color:"white"},
{id:3,count:2,color:"black"},{id:4,count:3,color:"red"},{id:5,count:2,color:"blue"}
];
var obj={};
arr.forEach(function(a){
obj[a.id]=obj[a.id]||[0];
obj[a.id][0]=obj[a.id][0]+a["count"];
obj[a.id][1]=a.color;
})
//console.log(obj);
var brr=Object.keys(obj).map(function(a){
return {"id":a,"count":obj[a][0],"color":obj[a][1]};
})
console.log(brr);
You could use a hash table for the result. For an ordered result, you could sort the result.
var array = [{ id: 2, cnt: 2 }, { id: 3, cnt: 3 }, { id: 4, cnt: 2 }, { id: 1, cnt: 6 }, { id: 2, cnt: 7 }, { id: 5, cnt: 4 }, { id: 2, cnt: 4 }, { id: 3, cnt: 2 }, { id: 4, cnt: 2 }, { id: 3, cnt: 2 }, { id: 4, cnt: 3 }, { id: 5, cnt: 2 }],
grouped = [];
array.forEach(function (a) {
if (!this[a.id]) {
this[a.id] = { id: a.id, cnt: 0 };
grouped.push(this[a.id]);
}
this[a.id].cnt += a.cnt;
}, Object.create(null));
grouped.sort(function (a, b) { return a.id - b.id; });
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Create new array from iterating JSON objects and getting only 1 of its inner array

See jsfiddle here: https://jsfiddle.net/remenyLx/2/
I have data that contains objects that each have an array of images. I want only the first image of each object.
var data1 = [
{
id: 1,
images: [
{ name: '1a' },
{ name: '1b' }
]
},
{
id: 2,
images: [
{ name: '2a' },
{ name: '2b' }
]
},
{
id: 3
},
{
id: 4,
images: []
}
];
var filtered = [];
var b = data1.forEach((element, index, array) => {
if(element.images && element.images.length)
filtered.push(element.images[0].name);
});
console.log(filtered);
The output needs to be flat:
['1a', '2a']
How can I make this prettier?
I'm not too familiar with JS map, reduce and filter and I think those would make my code more sensible; the forEach feels unnecessary.
First you can filter out elements without proper images property and then map it to new array:
const filtered = data1
.filter(e => e.images && e.images.length)
.map(e => e.images[0].name)
To do this in one loop you can use reduce function:
const filtered = data1.reduce((r, e) => {
if (e.images && e.images.length) {
r.push(e.images[0].name)
}
return r
}, [])
You can use reduce() to return this result.
var data1 = [{
id: 1,
images: [{
name: '1a'
}, {
name: '1b'
}]
}, {
id: 2,
images: [{
name: '2a'
}, {
name: '2b'
}]
}, {
id: 3
}, {
id: 4,
images: []
}];
var result = data1.reduce(function(r, e) {
if (e.hasOwnProperty('images') && e.images.length) r.push(e.images[0].name);
return r;
}, [])
console.log(result);
All answers are creating NEW arrays before projecting the final result : (filter and map creates a new array each) so basically it's creating twice.
Another approach is only to yield expected values :
Using iterator functions
function* foo(g)
{
for (let i = 0; i < g.length; i++)
{
if (g[i]['images'] && g[i]["images"].length)
yield g[i]['images'][0]["name"];
}
}
var iterator = foo(data1) ;
var result = iterator.next();
while (!result.done)
{
console.log(result.value)
result = iterator.next();
}
This will not create any additional array and only return the expected values !
However if you must return an array , rather than to do something with the actual values , then use other solutions suggested here.
https://jsfiddle.net/remenyLx/7/

How to get object with largest property from array?

I have an array of objects, with each object containing other 'subobjects'. I need to return the subobject with the largest 'quality' property.
The code below logs out all subobjects. How do I only return the one with the largest quality?
var maxQuality = function(Arr) {
Arr.forEach(function(obj, index) {
Math.max.apply(Math, obj.products.map(function(subObj) {
console.log(subObj);
}))
});
},
store = [
{
products: [
{
quality: 1,
info: 'info 1'
},
{
quality: 2,
info: 'info 2'
},
{
quality: 3,
info: 'info 3'
}
]
}
],
maxQualityProduct = maxQuality(store);
You can do that using the reduce() method on the array
var products = [
{
quality: 1,
info: 'info 1'
},
{
quality: 2,
info: 'info 2'
},
{
quality: 3,
info: 'info 3'
}
];
var highest = products.reduce(function(prev, current) {
return prev.quality > current.quality ? prev : current
}, {});
console.log(highest);
Note that reduce takes two parameters - one is the callback and the second is the initial item that you start with called seed. In this case, since we are only checking a flat value, a seed of an empty object will work fine since when the property quality is taken from it it would return undefined and that would be less than any of the other products. However, for more complex structures or comparisons, you might need to give an actual item from the array as a seed.
Although the other .reduce answer probably gives you what you need, if the store array were to contain more than one object - not sure if that is something you will want/need - you could use this:
var maxQuality = function(storeArray) {
function highestQuality(prev, curr) {
return prev.quality > curr.quality ? prev : curr
}
return storeArray.map(function(obj) {
return obj.products.reduce(highestQuality)
}).reduce(highestQuality);
}
I guess that boils down to a simple Array.prototype.sort kind of thing:
products.sort(( a, b ) => b.quality - a.quality )[ 0 ];
You can use following snippet:
var maxQuality = function(Arr) {
var result = undefined;
for (var obj of store) {
for (var product of obj.products) {
if (!result || product.quality > result.quality) {
result = product;
}
}
}
return result;
},
store = [
{
products: [
{
quality: 1,
info: 'info 1'
},
{
quality: 2,
info: 'info 2'
},
{
quality: 3,
info: 'info 3'
}
]
}
],
maxQualityProduct = maxQuality(store);
console.log(maxQualityProduct);

How to add each key pair from this Object into an Array as new Objects?

http://codepen.io/leongaban/pen/xwwdXr?editors=101
I have an API that returns an Object that looks like this:
{ unsure: 5, products: 25, trend: 124 }
What I need to do is take those key/value pairs and add those into an Array so they come out like so:
catArray = [
{ unsure: 5 },
{ products: 5 },
{ trend: 5 },
]
And better yet:
catArray = [
{
name: unsure,
count: 5
},
{
name: products,
count: 15
},
{
name: trend,
count: 50
}
]
Current codepen code:
var categories = { unsure: 5, products: 25, trend: 124 }
var catArray = [];
for (var i in categories) {
console.log(i + ":" + categories[i]);
catArray[i] = categories[i];
}
// vm.categories = Object.keys(data.data.categories);
console.log('catArray =',catArray);
At the moment I'm getting back some strange Array with a length of 0, my goal is an Array that has objects with which I can then iterate and build out a list with ng-repeat in my Angular app.
Pretty close, you can do:
var data = { unsure: 5, products: 25, trend: 124 }
var newData = [];
for (var key in data) {
newData.push({
name: key,
count: data[key]
});
}
Or, a different approach
var newData = Object.keys(data).map(function(key) {
return {
name: key,
count: data[key]
}
});
Demo: http://jsfiddle.net/gnrchtvj/
You can use this way
var categories = { unsure: 5, products: 25, trend: 124 }
var catArray = [];
for (var i in categories) {
if (categories.hasOwnProperty(i)) {
alert(i + " -> " + categories[i]);
catArray.push({name:i,value:categories[i]});
}
}
// vm.categories = Object.keys(data.data.categories);
console.log(catArray);

Convert object to JSON Array?

I have the following object being returned. I am counting a list of names by reading from a json file and storing the results in a new object.
{
ted: 501,
jeff: 40,
tony: 90
}
The following function creates an object with the names as properties and the count as their values.
function countNames(json){
var count = {};
for (var i = 0, j = json.length; i < j; i++) {
if (count[json[i].name]) {
count[json[i].name]++;
}
else {
count[json[i].name] = 1;
}
}
return count;
}
I need to create an array of objects that generate a result like this.
[
{
name: 'ted',
total: 501
},
{
name: 'jeff',
total: 40
}
{
name: 'tony',
total: 90
}
]
I am not sure what the best approach and most efficient way of achieving this is. Any help is appreciated.
Consider this following Javascript snippet:
for (var item in obj) {
result.push({
name: item,
total: obj[item]
});
}
Working DEMO
Output:
[
{
"name":"ted",
"total":501
},
{
"name":"jeff",
"total":40
},
{
"name":"tony",
"total":90
}
]
I don't understand how your code example relates to the question, but this turns the data in the first format into the last format:
var output = Object.keys(input).map(function(key) {
return {
name: key,
count: input[key]
}
});
it uses functional programming style, which usually leads to cleaner code.
JSBin

Categories

Resources