Code Guy
Code Guy

Reputation: 3198

Get maximum of array values by iterating all objects of JSON

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

Answers (3)

Mulan
Mulan

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

Nick
Nick

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

Soham
Soham

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

Related Questions