Javascript, фиксированный на один десятичный знак, за исключением случаев, когда он равен 0

#javascript

#javascript

Вопрос:

Мне нужно исправить число с десятичными дробями до 1 десятичного знака, поэтому я попробовал это:

 const convert = (numberWithDecimal) => numberWithDecimal
  .toFixed(1)
  .replace('.', ',')
  .replace(/(d)(?=(d{3}) (?!d))/g, "$1.");

console.log(convert(123.123)); // "123,1" that's ok
console.log(convert(123.005)); // "123,0" that's NOT ok  

Код здесь: https://jsfiddle.net/pmiranda/sp0fLrhb/3/
Но, когда десятичные дроби округляются до 0, я не хочу его использовать, например:

В 123.005 мне нужно получить 123 , а не 123.0

Любая помощь?

Комментарии:

1. .заменить(/,0$/,’ «)

2. Итак, насколько сложно .0 заменить пустую строку? 😉

3. Кто-то отредактировал мой вопрос с неправильной идеей

4. рассмотрим number.toLocaleString для локализованных десятичных и тысячных разделителей

Ответ №1:

Вся цель .toFixed функции — обеспечить фиксированное количество десятичных знаков, например, при отображении валюты (вы часто будете отображать 25,00 долларов вместо 25 долларов)

Если вам не нужно фиксированное количество десятичных знаков (например, вы иногда хотите 0, а иногда хотите 1), то вы не хотите использовать .toFixed . Вместо этого попробуйте использовать умножение и округление, чтобы получить желаемый результат:

 const convert = (numberWithDecimal) => String(
        Math.round(numberWithDecimal * 10) / 10
    )
    .replace(".", ",");
console.log(convert(123.123));
console.log(convert(123.005));  

Интересное замечание о производительности

Мне нравится профилировать решения, когда это возможно, и на первый взгляд казалось, что мое решение будет быстрее (я просто выполняю некоторую целочисленную арифметику, в то время как вы выполняете несколько строковых функций). Итак, я написал следующее:

 var d0 = new Date();
for (var i = 0; i < 1000000; i  ) { convert(123.123); }
console.log(new Date() - d0);
  

Затем я протестировал наши два решения. На моей машине ваше решение фактически работало примерно на 15% быстрее, чем у меня. Я не знаю почему, но если вы заинтересованы в максимальной производительности, то ваше решение из нескольких .replace() цепочек на самом деле работало быстрее (в Firefox на Mac OS X)

Примечание 2 о производительности

Если вы решите использовать Math.round() решение, но хотите, чтобы оно работало немного быстрее, в JavaScript есть хак для быстрого округления чисел:

 Math.round(123.123); // 123
Math.round(123.987); // 124

(123.123   0.5)|0; // 123
(123.987   0.5)|0; // 124
  

Обратите внимание, что этот взлом фактически завершается неудачей с отрицательными числами. Но пока вы имеете дело с положительными числами, этот метод быстрее, чем вызов Math.round() (и получает производительность примерно на уровне вашего решения

Это работает, потому что побитовые операторы в JavaScript ( amp; , | , ^ , ~ ) работают только с целочисленными значениями. Если передано нецелое число, они сначала усекут значение (отрежут все десятичные точки). Поскольку мы хотим округлять, а не усекать, мы добавляем 0,5 перед усечением.

Комментарии:

1. Хорошее решение, можете ли вы подробнее рассказать об интуиции при умножении и делении и о том, как вы к этому пришли

2. Math.round() Функция будет округляться до ближайшего целого числа, а не до ближайшей десятичной точки. Умножение на 10 сдвигает десятичную точку на одно место вправо. Деление на 10 сдвигает десятичную точку на одно место влево. Итак, я сдвинул десятичную точку, затем округлил, затем сдвинул десятичную точку обратно

Ответ №2:

Я думаю, очевидным простым решением является добавление replace(',0','') в цепочку.

 const convert = (numberWithDecimal) => numberWithDecimal
  .toFixed(1)
  .replace('.', ',')
  .replace(',0', '')
  .replace(/(d)(?=(d{3}) (?!d))/g, "$1.");

console.log(convert(123.123)); // "123,1" that's ok
console.log(convert(123.005)); // "123,1" that's ok