Home > Software engineering >  What are ways of comparing number values where some come in the form of e.g. '32k' or 
What are ways of comparing number values where some come in the form of e.g. '32k' or 

Time:01-06

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 the original value and the parsed value.

  • ... to finally sort the mapped array-items descending by each item's parsed value and assign the sorted array's first item's origin value as the maximum 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; }

  • Related