Is there a standard way in JavaScript to easily compare two numbers where one or both can be in the form of e.g. '32k'
. For example:
'32k' > 310
'2.5k' > 450
300 > 200
'2.5m' > '2.5k'
So that one easily could find the max
value of an array like ... [300, '3k', '3.2k']
. I could try to create my own function but I wondered if there is a standard way to do this?
CodePudding user response:
If you have 'k', or 'm', then the "number" is actually a string object, so you would need to parse out the number before you could do a comparison. Since you are only looking for the max value, a simple loop, parse, convert, and compare should be sufficient. Here is an example that supports 'k', 'm', 'b', 't' (case insensitive).
//Using a simple object for fast lookups of mutliplier.
const abvMap = {
'k': 1000,
'm': 1000000,
'b': 1000000000,
't': 1000000000000
};
function getMaxIndex(numArray) {
if (!numArray || !Array.isArray(numArray) || numArray.length === 0) {
throw 'invalid input';
}
//Start with the first number being the
let maxValue = parseNumber(numArray[0]);
let maxIdx = 0;
for (let i = 1, len = numArray.length; i < len; i ) {
let num = parseNumber(numArray[i]);
if (num > maxValue) {
maxValue = num;
maxIdx = i;
}
}
return maxIdx;
}
function parseNumber(numString) {
//Check to see if input is a number already. If so, return it.
if (typeof numString === 'number') {
return numString;
}
//Capture the number by removing any letters, $ sign (make it more reusable)
let num = parseFloat(numString.replace(/[a-zA-Z$]/gi, ''));
//Capture the number abbreviation.
let abv = numString.replace(/[-$\d.\s]*/gi, '');
if (abv) {
//Normalize abbreviation.
abv = abv.toLowerCase();
//Get the multiplier.
let multiplier = abvMap[abv];
if (multiplier) {
//Multiply the value accordingly.
return num * multiplier;
}
}
return num;
}
function getMaxValue(numArray) {
//Get the index of the max value, then return that value from the array.
return numArray[getMaxIndex(numArray)];
}
//Test array of mixed number values.
var myArray = ['300', '3k', '3.2k', '0.0001k', '$15', '$30k', 1000];
document.body.innerHTML = getMaxValue(myArray);
CodePudding user response:
One possible approach for a straightforward implementation was ...
... to provide a single, mostly regex and lookup based, parse functionality which computes number values from either real numbers or certain string-based number-formats.
... to
map
an array of possible number representatives by creating each an object which contains theorigin
al value and theparsed
value.... to finally
sort
the mapped array-items descending by each item'sparsed
value and assign the sorted array's first item'sorigin
value as themax
imum value.
The used regex ... /(?<value>\d (?:\.\d )?)(?<unit>[kmbt]?)/
... utilizes named capturing groups for an input-value's number-value
and numeral-unit
part.
function parseEnUSNumeralValue(input) {
// en-US based numeral abbreviation to multiplier lookup.
const enUSNumeralLookup = {
k: 1_000,
m: 1_000_000,
b: 1_000_000_000,
t: 1_000_000_000_000,
};
const { value, unit } = (/(?<value>\d (?:\.\d )?)(?<unit>[kmbt]?)/)
// see ... [https://regex101.com/r/xzlOqk/1]
.exec(String(input))
?.groups ?? {}
return (
parseFloat(value) * (enUSNumeralLookup[unit] ?? 1)
);
}
const numeralValues =
['300', '3k', '3.2k', '0.0001k', '$15', '$30k', 1000];
const [{ origin: max }] = numeralValues
.map(value => ({
origin: value,
parsed: parseEnUSNumeralValue(value),
}))
.sort((a, b) => b.parsed - a.parsed)
// result ... original `max` value.
console.log({ max });
// in order to display the approach/implementation behind.
console.log(
'mapped and descending sorted originally provided array ...',
numeralValues
.map(value => ({
origin: value,
parsed: parseEnUSNumeralValue(value),
}))
.sort((a, b) => b.parsed - a.parsed)
)
.as-console-wrapper { min-height: 100%!important; top: 0; }