Reputation: 3198
I have a JSON where I need to get the single maximum value of the maximum number. The below code displays maximum of each item, but I need to get the Max out of all the items. Here the expected answer is 78
for(var x in items) {
console.log("Max is" + Math.Max(items[x]));
}
var items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
Upvotes: 0
Views: 135
Reputation: 135217
I think you'll enjoy the simplicity and efficiency of this algorithm -
const items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
const nums =
Object.values(items).flatMap(function (x) {
if (Array.isArray(x))
return x
else
return []
})
const result =
Math.max(-Infinity, ...nums)
console.log(result)
// 78
If items
does not contain any numbers, the result will be -Infinity
generators
Here's another interesting approach using generators, if they are allowed. I think generators are particularly useful in this situation because input
is a complex object. items
is nested and some of the values we need to compare are non-numeric. It'd be nice we had a values
function that could produce a stream of all nested values -
const values = function* (t)
{ if (Array.isArray(t))
for (const v of t)
yield* values(v)
else if (Object(t) === t)
for (const v of Object.values(t))
yield* values(v)
else
yield t
}
const items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
let r = -Infinity
for (const v of values(items))
if (Number(v) === v)
r = Math.max(r, v)
console.log(r)
// 78
Generators return an iterator. An iterator gives us one value and will not compute the next one until we ask for it. Generators are called "lazy" because of this. Note we can use for..of
to iterate through a generator, exactly as we do with arrays.
But if you use this technique, don't stop here. Even though generators do not return arrays, we can write code that allows us to conceptualise the two similarly.
We could write general functions like filter
to select values matching a predicate, eg isNumber
-
const isNumber = function(x)
{ return Number(x) === x
}
const filter = function* (test, t)
{ for (const v of t)
if (test(v))
yield v
}
for (const v of filter(isNumber, values(items)))
console.log(v)
0
78
1
2
10
2
10
56
5
56
And we could write reduce
-
const reduce = function(f, init, t)
{ let r = init
for (const v of t)
r = f(r, v)
return r
}
const result =
reduce
( Math.max
, -Infinity
, filter(isNumber, values(items))
)
console.log(result)
// 78
Run the snippet below to verify the results in your own browser -
const values = function* (t)
{ if (Array.isArray(t))
for (const v of t)
yield* values(v)
else if (Object(t) === t)
for (const v of Object.values(t))
yield* values(v)
else
yield t
}
const filter = function* (test, t)
{ for (const v of t)
if (test(v))
yield v
}
const reduce = function(f, init, t)
{ let r = init
for (const v of t)
r = f(r, v)
return r
}
const isNumber = function(x)
{ return Number(x) === x
}
const items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
const result =
reduce
( Math.max
, -Infinity
, filter(isNumber, values(items))
)
console.log(result)
// 78
Upvotes: 2
Reputation: 147156
You can use Object.values
to get the values of each property of your object; filter those values to only return the arrays (using Array.isArray
), and then use Array.reduce
to find the maximum value over all the arrays:
var items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
const max = Object.values(items).filter(v => Array.isArray(v)).reduce((c, v) => Math.max(c, ...v), Number.MIN_SAFE_INTEGER);
console.log(max);
This can be written without the filter
by incorporating the logic into the reduce
:
var items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
const max = Object.values(items).reduce((c, v) => Array.isArray(v) ? Math.max(c, ...v) : c, Number.MIN_SAFE_INTEGER);
console.log(max);
If you can't use ES6 arrow functions, this will work:
var items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
const max = Object.values(items).filter(function(v) {
return Array.isArray(v);
}).reduce(function(c, v) {
return Math.max(c, ...v);
}, Number.MIN_SAFE_INTEGER);
console.log(max);
If the spread operator is also unavailable, you can use Array.reduce
again to get the maximum value from the array:
var items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
const max = Object.values(items).filter(function(v) {
return Array.isArray(v);
}).reduce(function(c, v) {
return v.reduce(function(a, b) {
return Math.max(a, b);
}, c);
}, Number.MIN_SAFE_INTEGER);
console.log(max);
Upvotes: 3
Reputation: 725
A little less complex code for newbies:
var items = {
"title": "Find Max",
"A": [0, 78],
"Z": [1],
"R": [2, 10],
"N": [2, 10, 56],
"P": [5, 56]
}
let array = [];
for (const property in items) {
if (Array.isArray(items[property])) {
let newArray = items[property];
array = [...array, ...newArray]
}
}
console.log(Math.max(...array)) // 78
Upvotes: 1