Getting keys with maximum value in JavaScript hashmap/object - javascript

Example:
hash = {'Apple':2, 'Orange' :1 , 'Mango' : 2}
here the maximum key is both Apple and Mango. How do I write a function which gives both Apple and Mango as answer.
I tried something like this :
Object.keys(hash).reduce(function(a, b){ return hash[a] > hash[b] ? a : b });
But this gives only Apple as the answer.

You could first calculate the max value as a separate operation and then just filter:
const hash = {Apple: 2, Orange: 1, Mango: 2};
const max = Object.keys(hash).reduce((a, v) => Math.max(a, hash[v]), -Infinity);
const result = Object.keys(hash).filter(v => hash[v] === max);
console.log(result);
Simple and readable, but it requires and extra iteration, so it's not the most efficient implementation.

Your reduce can only return a single value, but you need an array of values. So create one as the initial value.
In your function body, first, check whether your next key has a larger value, in which case you clear out the array.
Then, if your array is empty, or if the first contained key (and thus all keys) have the same value, push that key into your array.
Also, it helps if you give your arguments more expressive names.
Object.keys(hash).reduce(function(longestKeys, key){
if(hash[key] > hash[longestKeys[0]]){
longestKeys.length = 0;
}
if(longestKeys.length === 0 || hash[key] === hash[longestKeys[0]]){
longestKeys.push(key);
}
return longestKeys;
}, []);

You might transform the object into one whose properties correspond to the count, whose values are the (original) property names in an array, and then get the value of the property with the maximum count:
const hash = {'Apple':2, 'Orange' :1 , 'Mango' : 2};
const indexedByCount = Object.entries(hash).reduce((a, [key, val]) => {
if (!a[val]) a[val] = [];
a[val].push(key);
return a;
}, {});
console.log(
indexedByCount[Math.max(...Object.keys(indexedByCount))]
);
A less functional but more efficient method would be to keep track of a max variable corresponding to the maximum value found so far:
const hash = {'Apple':2, 'Orange' :1 , 'Mango' : 2};
let max = -Infinity;
console.log(
Object.entries(hash).reduce((a, [key, val]) => {
if (val > max) {
max = val;
return [key];
}
if (val === max) a.push(key);
return a;
}, [])
);

This works. If max changes clear the result and set only the max value.
var hash = {'Apple':2, 'Orange':1 , 'Mango':2, "Jackfruit":10, "Pineapple":5, "Tomato":-4};
var max="";
var result = Object.keys(hash).reduce(function(acc, val){
if(max < hash[val]) (max=hash[val], acc={});
if(hash[val]==max) acc[val] = hash[val];
return acc;
},{});
console.log(result)

I believe the requirements are traverse the array only once and results is an array of keys
const hash = {'Apple':2, 'Orange' :1 , 'Mango' : 2};
let max = 0;
let result = [];
Object.getOwnPropertyNames(hash).forEach(k => {
if (hash[k] > max) {
result = [k];
max = hash[k];
} else if (hash[k] === max) {
result.push(k);
}
});
console.log(result);

Related

Find min(max) value amoung all object.properties and return result as object or (modify original object)

This a bit easy task at first look, with finding the min/max values in Object, has become a challenge for me.
I have the following object (with 3 fixed keys):
let object_ = {
vendor: 5, //(Number),
market: 2, //(Number)
derivative: {
price: 15 //(Number)
}
}
And I am trying to find not just the min/max value, but return it as an object / modify the original one, which should looks like that:
result for min: //as an object
{ market: 2}
result for max: //as an object
derivative: {
price: 15 //(Number)
}
As for now, I have the following code which solves this problem by creating an Array from original object's properties (by manually checking each property's name), like:
/** Create an array of objects from object_*/
let array = [
{market: 2},
{vendor: 5},
{derivative: 15}
]
/** Find the necessary values in array with map and reduce*/
And then finding min/max values from all objects inside array, but the problem is that I should convert result of my code back to the original schema like:
if (resultObject.property === 'derivative') {
resultObject.derivative = {
price: resultObject.derivative //15
}
}
So the question is:
Am I on the right path, should I create an Array of objects for this case? Cause it's important for me, return not just the number itself, but with a custom named property.
Maybe someone has more better/shorter solution with ES6 syntax or modifying the original object with delete not min (or max) properties. Or can point me a way in to it?
You could get the value and path to it, select min and max value and rebuild objects for min and max.
This approach works only for a single min and max values.
const
getValues = object => {
return Object
.entries(object)
.flatMap(([k, v]) => v && typeof v === 'object'
? getValues(v).map(([keys, v]) => [[k, ...keys], v])
: [[[k], v]]
);
},
setValue = (target, [keys, value]) => {
const last = keys.pop();
keys.reduce((o, k) => o[k] = o[k] || {}, target)[last] = value;
return target;
},
getMinMax = object => {
const
[min, max] = getValues(object)
.reduce((r, v, i) => {
if (!i) return [v, v];
if (v[1] < r[0][1]) r[0] = v;
if (v[1] > r[1][1]) r[1] = v;
return r;
}, [])
.map(v => setValue({}, v));
return { min, max };
},
object = { vendor: 5, market: 2, derivative: { price: 15 } },
result = getMinMax(object);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
At the start of the function set min index to 0. Then loop through every key in the object from index 1. Test if the value at the key is an object, and if it is call the current function for it, and set the value to the returned value.Then test if the value is less then one at the index. If it is set index to i. At the end return an object with the value.
function getMin(obj) {
let minIndex = 0;
let keys = Object.keys(obj); //Get all the keys in the object
for(let i = 0;i < keys.length;i++) { // Loop through the keys
let value = obj[keys[i]];
if(typeof value !== 'number') { // If it's not a number get the minimum value in the object
value = getMin(value);
}
if(value < obj[keys[minIndex]]) minIndex = i; //If the value is less than the value at the min index, set the min index to the current index
// You can just change the sign to greater for the max function
}
let returnObject = {};
returnObject[keys[minIndex]] = obj[keys[minIndex]]; // Sets the object so it doesn't only return the value
return returnObject;
}
My own solution that I am using right now. It does the job well, but it's a bit monstrous I guess. So I won't accept it as an answer on my own question. But it still an option anyway.
let object = {
market: 5,
vendor: 2,
derivative: {
price: 15
}
};
let bufferArray = [];
for (const property in object) {
if (typeof object[property] === 'object') {
bufferArray.push({name: property, value: object[property].price});
} else {
bufferArray.push({name: property, value: object[property]});
}
}
let {name, value} = bufferArray.reduce((prev, curr) => prev.value < curr.value ? prev : curr); //just change < to > for max value
let newObject = {[name]: value};
console.log(newObject); //{ vendor: 2 }

How to compare the highest value between different objects for each property and update the heights value?

I'm running a loop over data per row, each row the data can like so, I also have a var that stores the object with the highest value.
{"num":10,"num2":300}
{"num":50}
{"num":300,"num2":500, "num3": 75}
{"num":1,"num2":50}
I want to be able to get the highest value, so my results would be and I can update my var.
{"num":300,"num2":500, "num3": 75}
So I have one var that compares the keys if they match and the value is higher update it if not do nothing.
UPDATED,
Yes I have one object "max"
{"num":10,"num2":300}
and then I have another object "input"
{"num":300,"num2":500, "num3": 75}
I need to compare the first object max with input and match key properties to each other (num2) once matched if the input is higher update it and or if there is no match for example num3 is new save that one as well
I have the following
let filtered = Object.keys(input)
.filter(key => {
if (key.includes("date")) return false;
return !isNaN(input[key]);
}).reduce((output, key) => {
output[key] = input[key];
return output;
}, {});
This gives me the fist object I now need to compare it with the second object input and update everything.
This should work
let data = [
{"num":10,"num2":300},
{"num":50},
{"num":300,"num2":500, "num3": 75},
{"num":1,"num2":50}
]
let filtered = Object.keys(input)
.filter(key => {
if (key.includes("date")) return false;
return !isNaN(input[key]);
}).reduce((output, key) => {
Object.entries(input).forEach(([key, value]) => {
const highest = output[key];
if (highest === undefined || highest < value) {
output[key] = value
}
})
return output;
}, {});
console.log(JSON.stringify(result))
UPDATE
let output = {}
let firstInput = {"num":10,"num2":300}
let secondInput = {"num":0,"num2":0}
let thirdInput = {"num":11,"num2":301}
function fun(input, output){
Object.keys(input).forEach(function(key) {
if (
(output !== undefined && output[key] === undefined) ||
output[key] < input[key]
) {
output[key] = input[key]
}
});
return output
}
console.log(JSON.stringify(output))
output = fun(firstInput, output)
console.log(JSON.stringify(output))
output = fun(secondInput, output)
console.log(JSON.stringify(output))
output = fun(thirdInput, output)
console.log(JSON.stringify(output))
By using Math.max, wrapped inside a function which accepts the name of the property.
var arr = [
{"num": 10, "num2": 300},
{"num": 50},
{"num": 300,"num2": 500,"num3": 75},
{"num": 1,"num2": 50}
];
function getMax(prop) {
return Math.max.apply(Math, arr.map(o => !isNaN(o[prop]) && o[prop]));
}
console.log(getMax("num"));
For getting a max on number of properties, modify the method slightly to the below... first store the max of each property, then get the final max.
var arr = [
{"num": 10, "num2": 300},
{"num": 50},
{"num": 300, "num2": 500, "num3": 75 },
{"num": 1, "num2": 50}
];
function getMax(props) {
var values = [];
for (var i = 0; i < props.length; i++) {
values.push(Math.max.apply(Math, arr.map(o => !isNaN(o[props[i]]) &&
o[props[i]])));
}
return Math.max.apply(Math, values);
}
console.log(getMax(["num", "num2"]));
I think you are looking for something like this, right?
const rowData1 = {a: 10, 'b': 100};
const rowData2 = {a: 11};
const rows = [
rowData1,
rowData2
];
// Output {a: 11, b: 100 }
rows.reduce((highestValues, rowData) => {
Object.entries(rowData).forEach(([key, value]) => {
const highest = highestValues[key];
if (highest === undefined || highest < value) {
// Not assigned yet or smaller
highestValues[key] = value
}
})
return highestValues
}, {});

How to find highest key in JSON?

An API I'm working with is returning poorly structured data like this:
{
"scsi0": "vm-101-disk-1.qcow2,size=32G",
"scsi1": "vm-101-disk-2.qcow2,size=32G",
"scsi2": "vm-101-disk-3.qcow2,size=32G"
}
As you can see, instead of having a scsi object with then contains the 0, 1, 2 key/value pairs, I just have the keys named like that.
In JavaScript, how can I search for the highest scsi value which in that case would be 2?
Object.keys() is a good place to start for jobs like this. How about something like this?
var data = {
"scsi0": "vm-101-disk-1.qcow2,size=32G",
"scsi1": "vm-101-disk-2.qcow2,size=32G",
"scsi2": "vm-101-disk-3.qcow2,size=32G"
};
// get keys of data
var keys = Object.keys(data);
// get array of number values
var numbers = keys.map(function(key) {
// strip text out of keys, parse as integers
return parseInt(key.replace(/\D/g, '') || 0);
})
// get the largest number in the array
var highest = Math.max.apply(null, numbers);
// build the data key with this number
var key = "scsi" + highest;
// get the data pertaining to the key
var final = data[key];
// log the result
console.log(final);
You could use a collator to get a compare function that takes such embedded numbers into account:
const data = {
"scsi0": "vm-101-disk-1.qcow2,size=32G",
"scsi11": "vm-101-disk-2.qcow2,size=32G",
"scsi2": "vm-101-disk-3.qcow2,size=32G"
};
const cmp = (new Intl.Collator(undefined, {numeric: true})).compare;
const max = Object.keys(data).reduce((a, b) => cmp(a, b) > 0 ? a : b);
console.log(max)
Use reduce on keys of your object:
var o = {
"scsi0": "vm-101-disk-1.qcow2,size=32G",
"scsi11": "vm-101-disk-2.qcow2,size=32G",
"scsi2": "vm-101-disk-3.qcow2,size=32G"
};
var max = Object.keys(o).reduce((m, k) => +m.replace(/\D/g, '') > +k.replace(/\D/g, '') ? m : m = k);
console.log(max, +max.replace(/\D/g, ''))

push() won't work as expected in reduce()

Why doesn't a.push(b) work in my Array.reduce()? a=a.push(b) where b is a string, turns a to an integer.?!
getHighestValuesInFrequency: function(frequency) {
//Input:var frequency = {mats: 1,john: 3,johan: 2,jacob: 3};
//Output should become ['John','jacob']
var objKeys = Object.keys(frequency);
var highestVal = objKeys.reduce((a,b)=>
{highestVal = (frequency[b] > a)? frequency[b] : a;
return highestVal;},0);
var winner = objKeys.reduce((a,b)=>
{ a = (frequency[b] === highestVal)? a.push(b) : a;
return a},[]);
return winner;
}
Since push() returns the new length of the array, you're assigning the length to a. Instead of a conditional operator, use an if statement.
var winner = objKeys.reduce((a, b) => {
if (frequency[b] === highestVal) {
a.push(b);
}
return a;
}, []);
The push() returns the new length. You can use ES2015 spread syntax:
var winner = objKeys.reduce((a, b)=> {
a = (frequency[b] === highestVal)? [...a, b] : a;
return a
}, []);
Building on the excellent answer by #Alexander Moiseyev.
You can avoid setting and mutating both variables a and winner by doing the following:
return objKeys.reduce((acc, val) =>
frequency[val] === highestVal
? [...acc, val]
: acc
,[])
note: for clarity I have explicitly declared the accumulator and value in this reduce method.
Please note that this structure you provide is not clear enough
I would use instead an array of objects each having a name and a frecuency
var frequencies = [{name : "mats", frecuency : 1},
{name : "john", frecuency: 3},
{name : "johan", frecuency: 2},
{name : "jacob", frecuency: 3}];
Then you can use a filter operation and map to get what you need
var max = Math.max.apply(Math, frequencies.map(function(o){return o.frecuency;}));
var maxElems = frequencies.filter(function(a){return a.frecuency == max}).map(function(a){return a.name;});
maxElems will give you the names of the people with higher frecuency
The spread syntax enables an even shorter way:
const winner = objKeys.reduce((a, b) => {
return frequency[b] === highestVal ? [...a, b] : a
}, [])

How to sum the values of a JavaScript object?

I'd like to sum the values of an object.
I'm used to python where it would just be:
sample = { 'a': 1 , 'b': 2 , 'c':3 };
summed = sum(sample.itervalues())
The following code works, but it's a lot of code:
function obj_values(object) {
var results = [];
for (var property in object)
results.push(object[property]);
return results;
}
function list_sum( list ){
return list.reduce(function(previousValue, currentValue, index, array){
return previousValue + currentValue;
});
}
function object_values_sum( obj ){
return list_sum(obj_values(obj));
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = list_sum(obj_values(a));
var summed = object_values_sum(a)
Am i missing anything obvious, or is this just the way it is?
It can be as simple as that:
const sumValues = obj => Object.values(obj).reduce((a, b) => a + b, 0);
Quoting MDN:
The Object.values() method returns an array of a given object's own enumerable property values, in the same order as that provided by a for...in loop (the difference being that a for-in loop enumerates properties in the prototype chain as well).
from Object.values() on MDN
The reduce() method applies a function against an accumulator and each value of the array (from left-to-right) to reduce it to a single value.
from Array.prototype.reduce() on MDN
You can use this function like that:
sumValues({a: 4, b: 6, c: -5, d: 0}); // gives 5
Note that this code uses some ECMAScript features which are not supported by some older browsers (like IE). You might need to use Babel to compile your code.
You could put it all in one function:
function sum( obj ) {
var sum = 0;
for( var el in obj ) {
if( obj.hasOwnProperty( el ) ) {
sum += parseFloat( obj[el] );
}
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
var summed = sum( sample );
console.log( "sum: "+summed );
For fun's sake here is another implementation using Object.keys() and Array.reduce() (browser support should not be a big issue anymore):
function sum(obj) {
return Object.keys(obj).reduce((sum,key)=>sum+parseFloat(obj[key]||0),0);
}
let sample = { a: 1 , b: 2 , c:3 };
console.log(`sum:${sum(sample)}`);
But this seems to be way slower: jsperf.com
If you're using lodash you can do something like
_.sum(_.values({ 'a': 1 , 'b': 2 , 'c':3 }))
Now you can make use of reduce function and get the sum.
const object1 = { 'a': 1 , 'b': 2 , 'c':3 }
console.log(Object.values(object1).reduce((a, b) => a + b, 0));
A regular for loop is pretty concise:
var total = 0;
for (var property in object) {
total += object[property];
}
You might have to add in object.hasOwnProperty if you modified the prototype.
Honestly, given our "modern times" I'd go with a functional programming approach whenever possible, like so:
const sumValues = (obj) => Object.keys(obj).reduce((acc, value) => acc + obj[value], 0);
Our accumulator acc, starting with a value of 0, is accumulating all looped values of our object. This has the added benefit of not depending on any internal or external variables; it's a constant function so it won't be accidentally overwritten... win for ES2015!
Any reason you're not just using a simple for...in loop?
var sample = { a: 1 , b: 2 , c:3 };
var summed = 0;
for (var key in sample) {
summed += sample[key];
};
http://jsfiddle.net/vZhXs/
let prices = {
"apple": 100,
"banana": 300,
"orange": 250
};
let sum = 0;
for (let price of Object.values(prices)) {
sum += price;
}
alert(sum)
I am a bit tardy to the party, however, if you require a more robust and flexible solution then here is my contribution. If you want to sum only a specific property in a nested object/array combo, as well as perform other aggregate methods, then here is a little function I have been using on a React project:
var aggregateProperty = function(obj, property, aggregate, shallow, depth) {
//return aggregated value of a specific property within an object (or array of objects..)
if ((typeof obj !== 'object' && typeof obj !== 'array') || !property) {
return;
}
obj = JSON.parse(JSON.stringify(obj)); //an ugly way of copying the data object instead of pointing to its reference (so the original data remains unaffected)
const validAggregates = [ 'sum', 'min', 'max', 'count' ];
aggregate = (validAggregates.indexOf(aggregate.toLowerCase()) !== -1 ? aggregate.toLowerCase() : 'sum'); //default to sum
//default to false (if true, only searches (n) levels deep ignoring deeply nested data)
if (shallow === true) {
shallow = 2;
} else if (isNaN(shallow) || shallow < 2) {
shallow = false;
}
if (isNaN(depth)) {
depth = 1; //how far down the rabbit hole have we travelled?
}
var value = ((aggregate == 'min' || aggregate == 'max') ? null : 0);
for (var prop in obj) {
if (!obj.hasOwnProperty(prop)) {
continue;
}
var propValue = obj[prop];
var nested = (typeof propValue === 'object' || typeof propValue === 'array');
if (nested) {
//the property is an object or an array
if (prop == property && aggregate == 'count') {
value++;
}
if (shallow === false || depth < shallow) {
propValue = aggregateProperty(propValue, property, aggregate, shallow, depth+1); //recursively aggregate nested objects and arrays
} else {
continue; //skip this property
}
}
//aggregate the properties value based on the selected aggregation method
if ((prop == property || nested) && propValue) {
switch(aggregate) {
case 'sum':
if (!isNaN(propValue)) {
value += propValue;
}
break;
case 'min':
if ((propValue < value) || !value) {
value = propValue;
}
break;
case 'max':
if ((propValue > value) || !value) {
value = propValue;
}
break;
case 'count':
if (propValue) {
if (nested) {
value += propValue;
} else {
value++;
}
}
break;
}
}
}
return value;
}
It is recursive, non ES6, and it should work in most semi-modern browsers. You use it like this:
const onlineCount = aggregateProperty(this.props.contacts, 'online', 'count');
Parameter breakdown:
obj = either an object or an array
property = the property within the nested objects/arrays you wish to perform the aggregate method on
aggregate = the aggregate method (sum, min, max, or count)
shallow = can either be set to true/false or a numeric value
depth = should be left null or undefined (it is used to track the subsequent recursive callbacks)
Shallow can be used to enhance performance if you know that you will not need to search deeply nested data. For instance if you had the following array:
[
{
id: 1,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 2,
otherData: { ... },
valueToBeTotaled: ?
},
{
id: 3,
otherData: { ... },
valueToBeTotaled: ?
},
...
]
If you wanted to avoid looping through the otherData property since the value you are going to be aggregating is not nested that deeply, you could set shallow to true.
Use Lodash
import _ from 'Lodash';
var object_array = [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}];
return _.sumBy(object_array, 'c')
// return => 9
I came across this solution from #jbabey while trying to solve a similar problem. With a little modification, I got it right. In my case, the object keys are numbers (489) and strings ("489"). Hence to solve this, each key is parse. The following code works:
var array = {"nR": 22, "nH": 7, "totB": "2761", "nSR": 16, "htRb": "91981"}
var parskey = 0;
for (var key in array) {
parskey = parseInt(array[key]);
sum += parskey;
};
return(sum);
A ramda one liner:
import {
compose,
sum,
values,
} from 'ramda'
export const sumValues = compose(sum, values);
Use:
const summed = sumValues({ 'a': 1 , 'b': 2 , 'c':3 });
We can iterate object using in keyword and can perform any arithmetic operation.
// input
const sample = {
'a': 1,
'b': 2,
'c': 3
};
// var
let sum = 0;
// object iteration
for (key in sample) {
//sum
sum += (+sample[key]);
}
// result
console.log("sum:=>", sum);
A simple solution would be to use the for..in loop to find the sum.
function findSum(obj){
let sum = 0;
for(property in obj){
sum += obj[property];
}
return sum;
}
var sample = { a: 1 , b: 2 , c:3 };
console.log(findSum(sample));
function myFunction(a) { return Object.values(a).reduce((sum, cur) => sum + cur, 0); }
Sum the object key value by parse Integer. Converting string format to integer and summing the values
var obj = {
pay: 22
};
obj.pay;
console.log(obj.pay);
var x = parseInt(obj.pay);
console.log(x + 20);
function totalAmountAdjectives(obj) {
let sum = 0;
for(let el in obj) {
sum += el.length;
}
return sum;
}
console.log(totalAmountAdjectives({ a: "apple" }))
A simple and clean solution for typescrip:
const sample = { a: 1, b: 2, c: 3 };
const totalSample = Object.values(sample).reduce(
(total: number, currentElement: number) => total + currentElement
);
console.log(totalSample);
Good luck!

Categories