need some guidance please. trying to create recursive func to return max numeric value in any sort of collection of obj/str/num etc. and wants to include 2D-Arrays iterations also. Please suggest how to improve on existing code and where/how to include array iterations. Here is the snippet. Thanks
// maxNumInCollection -- Recursive
function RecursiveMaxNumInColl() {
// console.log(`Count of args : ${arguments.length}`);
let maxnum = Number.NEGATIVE_INFINITY;
for (let i = 0; i < arguments.length; i ) {
let argtype = typeof arguments[i];
let cElem = arguments[i]; // current element
// console.log(`'i' : ${i} : ${argtype} : ${cElem}`);
if (argtype == "object" && cElem !== null) {
for (let j = 0; j < cElem.length; j ) {
let newmaxnum = RecursiveMaxNumInColl(cElem[j]); // current sub element
maxnum = Math.max(maxnum, newmaxnum);
}
} else if (!isNaN(cElem)) {
maxnum = Math.max(maxnum, cElem);
}
}
return maxnum;
}
function GetMaxNum() {
let n1 = [1, "A", "1A"];
let n2 = [
[2, "2B", "B2", ""],
[3, "CC", "04", " "],
];
let n3 = ["030", "000", "-50", null, undefined, null];
let n4 = [null, null, undefined];
let n5 = 11;
let in1 = document.getElementById("in1").value;
let in2 = document.getElementById("in2").value;
let in3 = document.getElementById("in3").value;
// prettier-ignore
let output = RecursiveMaxNumInColl(22, "33", n1, n2, n3, n4, n5, in1, in2, in3);
console.log(output);
document.getElementById("output").innerHTML =
"Highest number value in collection is : <br/>" output "<br/> <br/>";
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>JS Functions</title>
<script src="script.js" defer></script>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<h5>output</h5>
<input type="number" name="n1" id="in1" />
<input type="number" name="n2" id="in2" />
<input type="number" name="n3" id="in3" />
<button id="btn1" onclick="GetMaxNum()">Get Max Num</button>
<div id="output"></div>
</body>
</html>
CodePudding user response:
In your question you ask for the max number in any collection of objects, strings, numbers, and arrays. The data in your example only shows arrays but doesn't accommodate for objects in the arrays, or objects that would contain subarrays. Let's see a decomposed approach that makes quick work of this problem -
const data =
[ 0, [7, {a: 1, b: [6]}], [{c: 4, d: [{e: 7, f: [{g: 9}]}]}], 3]
// ??
The first step is to take a generic input, t
, and return an iterable of all of its values. The way to do this is do a simple type-check on t
, if it is an Object or Array, recursively yield the child values. Otherwise t
is a simple value and so yield t
itself -
function *values(t) {
switch (t?.constructor) {
case Object:
case Array:
for (const v of Object.values(t))
yield *values(v)
break
default:
yield t
}
}
Now that we can examine all the values of t
, it's time to filter out the numbers. For all v
of the iterable values, if v
is a Number (and not NaN
), yield the number -
function *numbers(iterable) {
for (const v of iterable) {
switch (v?.constructor) {
case Number:
if (!Number.isNaN(v))
yield v
}
}
}
Finally we spread the numbers in the Math.max
and get the solution -
console.log(Math.max(...numbers(values(data))))
// 9
This is a useful approach because the two values
an numbers
functions are generic and can be easily reused in any part of your program that needs this functionality. Keeping functions smaller means they are easier to write and test as well!
You can run the demo below to verify the result in your own browser -
function *values(t) {
switch (t?.constructor) {
case Object:
case Array:
for (const v of Object.values(t))
yield *values(v)
break
default:
yield t
}
}
function *numbers(iterable) {
for (const v of iterable) {
switch (v?.constructor) {
case Number:
if (!Number.isNaN(v))
yield v
}
}
}
const data =
[ 0, [7, {a: 1, b: [6]}], [{c: 4, d: [{e: 7, f: [{g: 9}]}]}], 3]
console.log(Math.max(...numbers(values(data))))
Generators are terrific for a wide variety of problems. If you haven't learned about them or the yield
keyword yet, they may seem puzzling at first. Here's another way to express the solution above using ordinary functions -
const is = (t, T) =>
t?.constructor === T
const values = t =>
is(t, Object) || is(t, Array)
? Object.values(t).flatMap(v => values(v))
: [t]
const numbers = arr =>
arr.filter(v => is(v, Number) && !Number.isNaN(v))
const data =
[ 0, [7, {a: 1, b: [6]}], [{c: 4, d: [{e: 7, f: [{g: 9}]}]}], 3]
console.log(Math.max(...numbers(values(data))))
CodePudding user response:
Try this:
const RecursiveMaxNumInColl = (...args) => Math.max(
...args.flat(Infinity).map(x=> x).filter(x=>!Number.isNaN(x))
)
This flatten the array to its deepest values, map them to numbers, filter only those which conversion has succeeded and apply the Math.max function on them to get the greatest numerical value among them