skip to Main Content

I’m encountering an issue where my backend sends JSON data containing float numbers (e.g., 10.0, 20.0) to the UI side. However, upon receiving these numbers in the UI and logging them to the console, I observe that they are automatically converted to integers (10 and 20) instead of retaining their original float format. Strangely, numbers with a fractional part (e.g., 20.1) remain unchanged as 20.1.

I found the solution that I want. This is taken from stringify-with-floats npm library but modified to support any length of floats.

const stringifyWithFloats = (config = {}, decimals = 1) => {
  const beginFloat = '__FLOAT__BEGIN__';
  const endFloat = '__FLOAT__END__';

  return (inputValue, inputReplacer, space) => {
    const inputReplacerIsFunction = typeof inputReplacer === "function";
    let isFirstIteration = true;

    const jsonReplacer = (key, val) => {
      if (isFirstIteration) {
        isFirstIteration = false;
        return inputReplacerIsFunction ? inputReplacer(key, val) : val;
      }

      let value;
      if (inputReplacerIsFunction) {
        value = inputReplacer(key, val);
      } else if (Array.isArray(inputReplacer)) {
        value = inputReplacer.indexOf(key) !== -1 ? val : undefined;
      } else {
        value = val;
      }

      const forceFloat =
        config[key] === "float" &&
        (value || value === 0) &&
        typeof value === "number" &&
        !value.toString().toLowerCase().includes("e");

      return forceFloat ? `${beginFloat}${value}${endFloat}` : value;
    };

    const json = JSON.stringify(inputValue, jsonReplacer, space);

    const regexReplacer = (match, num) => {
      return num.includes(".") || Number.isNaN(num)
        ? Number.isNaN(num)
          ? num
          : Number(num).toFixed(decimals)
        : `${num}.${"0".repeat(decimals)}`;
    };

    const re = new RegExp(`"${beginFloat}(.+?)${endFloat}"`, "g");
    return json.replace(re, regexReplacer);
  };
};

// Test input
const testInput = {
  foo: 10.0,
  bar: {
    baz: 20.0,
    qux: {
      value: 30.0
    }
  }
};

// Configure the stringifyWithFloats function
const customStringify = stringifyWithFloats({ foo: 'float', 'bar.baz': 'float' });

// Serialize the test input
const serializedJson = customStringify(testInput, null, 2);

// Output the serialized JSON
console.log(serializedJson);

Output:

{
  "foo": 10.0,
  "bar": {
    "baz": 20,
    "qux": {
      "value": 30
    }
  }

}

So now the main issue and question. How to make this regex magic to handle nested objects and processes numbers as floats where required. AS you can see "foo" is shown correctly 10.0, but baz and value are not

2

Answers


  1. Chosen as BEST ANSWER

    I made a monster which gives me desired reuslt.This code provides a flexible way to serialize JavaScript objects into JSON strings while selectively formatting numeric values as floats based on a configurable set of rules (config). It handles nested objects recursively and ensures that numeric values are properly formatted using regular expressions after serialization.

    const stringifyWithFloats = (config = {}, decimals = 1) => {
      const beginFloat = '__FLOAT__BEGIN__';
      const endFloat = '__FLOAT__END__';
    
      return (inputValue, inputReplacer, space) => {
    const inputReplacerIsFunction = typeof inputReplacer === 'function';
    
    const jsonReplacer = (key, val, path) => {
      const currentPath = path.concat(key).join('.');
      let value;
      if (inputReplacerIsFunction) {
        value = inputReplacer(key, val);
      } else if (Array.isArray(inputReplacer)) {
        value = inputReplacer.indexOf(key) !== -1 ? val : undefined;
      } else {
        value = val;
      }
    
      const forceFloat =
        (config[currentPath] === 'float' || config[key] === 'float') &&
        (value || value === 0) &&
        typeof value === 'number' &&
        !value.toString().toLowerCase().includes('e');
    
      return forceFloat ? `${beginFloat}${value}${endFloat}` : value;
    };
    
    const deepJsonReplacer = (obj, path = []) => {
      if (typeof obj === 'object' && obj !== null) {
        return Object.keys(obj).reduce(
          (acc, k) => {
            acc[k] = deepJsonReplacer(obj[k], path.concat(k));
            return acc;
          },
          Array.isArray(obj) ? [] : {}
        );
      } else {
        return jsonReplacer(path[path.length - 1], obj, path.slice(0, -1));
      }
    };
    
    const json = JSON.stringify(deepJsonReplacer(inputValue), null, space);
    
    const regexReplacer = (match, num) => {
      return num.includes('.') || Number.isNaN(num)
        ? Number.isNaN(num)
          ? num
          : Number(num).toFixed(decimals)
        : `${num}.${'0'.repeat(decimals)}`;
    };
    
    const re = new RegExp(`"${beginFloat}(.+?)${endFloat}"`, 'g');
    return json.replace(re, regexReplacer);
      };
    };
    
    const generateFloatConfig = (obj, basePath = '') => {
      let config = {};
    
      for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      let newBasePath = basePath ? `${basePath}.${key}` : key;
    
      if (
        typeof obj[key] === 'object' &&
        obj[key] !== null &&
        !Array.isArray(obj[key])
      ) {
        Object.assign(config, generateFloatConfig(obj[key], newBasePath));
      } else if (typeof obj[key] === 'number') {
        config[newBasePath] = 'float';
      }
    }
      }
    
      return config;
    };
    
    const testInput = {
      foo: 10.0,
      bar: {
    baz: 20.0,
    qux: {
      value: 30.0
    }
      }
    };
    
    const floatConfig = generateFloatConfig(testInput);
    const customStringify = stringifyWithFloats(floatConfig);
    const serializedJson = customStringify(testInput, null, 2);
    
    console.log(serializedJson);


  2. in JavaScript, there is no integer and float as two different types. any number is of type number which may or may not contain decimals. the representation of the number will remove of tailing zeros of the decimal. so 1.010100 will logged as 1.01, 5.00 will logged as 5 etc. (since it the same number).
    If in the UI you want to show it with decimal of x points, you can convert it to string, round to nearest decimal and add tailing zeros if need.
    This can be done with the built-in function x.toFixed(1)

    Example:

    const x = 5;
    console.log(x) // 5
    console.log(x.toFixed(1)) // 5.0
    console.log(x.toFixed(3)) //5.000
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search