Cant use toLocaleString in nodejs - javascript

I've created a util lib to format a number.
this is the format lib
module.exports = {
format: function (number) {
let value = number.toString()
let teste = value.slice(0, -2) + '.' + value.slice(-2)
let newvalue = Number(teste)
return newvalue.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' })
}
}
in my file I import it ant use:
var format = require('../utils/format').format
let number = format(31231)
But it doesnt return the correct format R$2.312,31
Its returning R$2,312.31
If I run in JsFiddle its works as expected...Dont know what can be wrong

As mentioned in the comments, it looks like a bug in node - you could correct with
const reformat = s => s.replace(/[,.]/g, x => ({'.':',', ',':'.'})[x]);
console.log(reformat('R$2,312.31'))
You might also want to place a guard on the replacement:
s => /\.\d{2}$/.test(s) ? s.replace(/[,.]/g, x => ({'.':',', ',':'.'})[x]) : s
Use it in your lib like this:
module.exports = {
format: function (number) {
let value = number.toString()
let teste = value.slice(0, -2) + '.' + value.slice(-2)
let newvalue = Number(teste)
const reformat = s => /\.\d{2}$/.test(s) ? s.replace(/[,.]/g, x => ({'.':',', ',':'.'})[x]) : s
return reformat(newvalue.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' }))
}
}

Related

properly override toJSON for mathjs bignumber

I am trying to override mathjs Bignumber using:
import * as math from 'mathjs';
export const bgn = (v: number | math.BigNumber) => {
const z = math.bignumber(v) as math.BigNumber;
(z as any).toJSON = () => {
return Number(math.larger(100, z) ? math.round(z,2) : math.round(z,4)).toFixed(4);
}
return z;
}
but for some reason, it's still stringifying it to:
{"mathjs":"BigNumber","value":"42500"}
my goal is to stringify it to a number:
42500
This is not currently possible with the native JSON.stringify implementation. It will become possible with the adoption of the JSON.parse source text access proposal which also includes a helper for non-lossy serialization.
You'd use it as
const text = JSON.stringify(value, (key, val) => {
if (val instanceof math.bignumber) return JSON.rawJSON(val.toString())
else return val;
});
console.log(text);
OP was on the right track, this should work fine:
const math = require('mathjs');
const bgn = (v) => {
const z = math.bignumber(v); // as math.BigNumber;
(z).toJSON = () => {
return Number(math.larger(z, 100) ? math.round(z, 3) : math.round(z, 5));
}
return z;
}
console.log(JSON.stringify(bgn(5.555555444)));
in JS instead of TS.

Use Intl.NumberFormat without rounding

I'm using Intl.NumberFormat to format numbers:
const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 1,
maximumFractionDigits: 4,
minimumSignificantDigits: 1,
maximumSignificantDigits: 4
})
formatter.format(0.99999) // results: '1'. desired result: '0.9999'
formatter.format(0.006393555) // results: '0.006394'. desired result: '0.006393'
formatter.format(0.9972620384752073) // results: '0.9973'. desired result: '0.9972'
formatter.format(12345.67) // results: '12,350'. desired result: '12,345.67'
formatter.format(200001) // results: '200,000'. desired result: '200,001'
As you can see the numbers are being rounded automatically, which is undesirable behavior in my case.
Is there a way to tell the formatter not to round?
I Didn't found any option or combination of options to achieve that.
I don't think this is possible with current spec and there are few proposals for the new spec, but you can still use formatToParts method and add custom function to format number parts as you wish.
For your first use case it could look something like:
const trauncateFractionAndFormat = (parts, digits) => {
return parts.map(({ type, value }) => {
if (type !== 'fraction' || !value || value.length < digits) {
return value;
}
let retVal = "";
for (let idx = 0, counter = 0; idx < value.length && counter < digits; idx++) {
if (value[idx] !== '0') {
counter++;
}
retVal += value[idx];
}
return retVal;
}).reduce((string, part) => string + part);
};
const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 0,
maximumFractionDigits: 20
})
console.log(trauncateFractionAndFormat(formatter.formatToParts(0.99999), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(0.006393555), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(0.9972620384752073), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(12345.67), 4));
console.log(trauncateFractionAndFormat(formatter.formatToParts(20001), 4));
NumberFormat will always round up, but you can play around this one extra function.
function roundDownSignificantDigits(number, decimals) {
let significantDigits = (parseInt(number.toExponential().split('e-')[1])) || 0;
let decimalsUpdated = (decimals || 0) + significantDigits - 1;
decimals = Math.min(decimalsUpdated, number.toString().length);
return (Math.floor(number * Math.pow(10, decimals)) / Math.pow(10, decimals));
}
and then
const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 1,
maximumFractionDigits: 4,
minimumSignificantDigits: 1,
maximumSignificantDigits: 4
})
result:
formatter.format(roundDownSignificantDigits(0.99999,4)); // "0.9999"
formatter.format(roundDownSignificantDigits(0.006393555,4)); // "0.006393"
formatter.format(roundDownSignificantDigits(0.9972620384752073,4)); // "0.9972"
Sometimes the above solutions round off the value,
here is the simplest Solution I'm using and its working fine for me,
let a = "2.2652";// string
let b = 2.2652; // decimal-number
let c = 22200223.26522200225; // decimal-number
let d = 2 // non-decimal
const formatter = new Intl.NumberFormat('en-US', {
style: 'currency',
currency: 'USD',
minimumFractionDigits: 2,
maximumFractionDigits: 20,
minimumSignificantDigits: 1,
maximumSignificantDigits: 20
});
const newFunc = (val)=>{
val = formatter.format((val))
if(val.includes(".")){
let number = val.toString().split(".")[0]+"."+val.split(".")[1].slice(0, 2)
return number
}else{
return val
}
}
console.log(newFunc(a))
console.log(newFunc(b))
console.log(newFunc(c))
console.log(newFunc(d))
I could get it using formatToParts. You can also do some javascript Array functions tricks to separate out the decimal part if needed.
export const formatNumberWithoutDecimals = (price) =>
new Intl.NumberFormat(i18n.language, {
minimumFractionDigits: 2,
}).formatToParts(price).reduce((result, part) => (part.type !== "decimal" && part.type !== "fraction") ? result + part.value : result, "");

When the amount is not in USD currency don't have the $ symbol in the front

How can I add a condition in the following function --> When the currency is not USD don't add the $ sign in the front of the amount.
var convertToCurrency = number => {
if (!number) return '';
return new Intl.NumberFormat('en', {
style: 'currency',
currency: 'USD'
}).format(number);
};
var amount = {
amount: 10,
currency: "JPY"
};
convertToCurrency(amount["amount"]);
==> $10.00 JPY
If I understand what you want correctly you could add a flag to your function isUSD or something like this and then do the following.
const convertToCurrency = (number, isUSD) => {
if (!number) return '';
if (isUSD) {
return new Intl.NumberFormat('en', {
style: 'currency',
currency: 'USD'
}).format(number);
} else {
return (Math.round(number * 100) / 100).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ",");;
}
};
console.log(convertToCurrency(10000, true));
console.log(convertToCurrency(10000, false));
You can pass an optional boolean and either return full string or skip the first character (which is $ sign) if it's USD.
var convertToCurrency = (number, isUsd) => {
if (!number) return '';
var sliceFrom = isUsd ? 1 : 0;
return new Intl.NumberFormat('en', {
style: 'currency',
currency: 'USD',
}).format(number).slice(sliceFrom);
};
convertToCurrency(10000, true);
If you plan to use other formats for your currency conversion, you could also pass in format string and check against that.
So instead of isUsd, use currency
var convertToCurrency = (number, currency) => {
if (!number || !currency) return '';
var sliceFrom = currency === 'USD' ? 1 : 0;
return new Intl.NumberFormat('en', {
style: 'currency',
currency,
}).format(number).slice(sliceFrom);
};
convertToCurrency(10000,'USD');
var convertToCurrency = (number, currency) => {
if (!number || !currency) return '';
var isUSD = (currency.toUpperCase() === 'USD');
if (isUSD) {
return new Intl.NumberFormat('en', {
style: 'currency',
currency: 'USD',
}).format(number);
} else {
return new Intl.NumberFormat('en', {
style: 'decimal',
}).format(number) + ' ' + currency;
}
};
convertToCurrency(10000, 'USD'); // "$10,000.00"
convertToCurrency(10000, 'JPY'); // "10,000.00 JPY"
Some remarks:
Your current code (modifying the currency parameter) wouldn't produce "$ 100 JPY", but "¥100". Not quite the same…
Maybe your are too much tweaking the output, and rather, should just use the standard outputs. (most notably, put all the currencies at left or right, but avoid doing a mix)
You might be interested by the parameter currencyDisplay: 'symbol' / 'code'. The former outputs a symbol like "$" or "¥" if possible, the latter outputs an ISO code like "USD" or "JPY".
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/NumberFormat
var style = "currency";
var currency = 'USD'; //replace with your dynamic value
var convertToCurrency = (number, style, currency) => {
if (!number) return '';
return new Intl.NumberFormat('en', {
style: style,
currency: currency
}).format(number);
};
console.log(convertToCurrency(10000, style, currency));

How to properly type a .map loop argument

So I have a timezone list and has a type of the following:
type TimezoneListType = {
label: string;
name: string;
offset: number;
lower?: string;
offsetString?: string;
};
const TIMEZONE_LIST: TimezoneListType[] = [
{
label: 'Niue',
name: 'Pacific/Niue',
offset: -11,
},
{
label: 'Pago Pago',
name: 'Pacific/Pago_Pago',
offset: -11,
},
//... and so on
];
const TIMEZONE_NAME_MAP: any = {};
const TIMEZONE_MAP = TIMEZONE_LIST.map((item) => {
const positive = item.offset >= 0;
const hour = item.offset | 0;
const minute = (item.offset - hour) * 60;
return TIMEZONE_NAME_MAP[item.name] = { // typescript is screaming in this line. if try to change the `any` to `TimezoneListType`
...item,
lower: item.label.toLowerCase(),
offsetString: 'something',
};
});
on the lower end of the code you'll see that I'm transforming timezone list to have keys of item.name then adding some property lower and offsetString.
My problem is item.name is giving me:
Element implicitly has an 'any' type because index expression is not of type number ts(7015)
on both item and name when I hover at them. And I'm not sure how to type it correctly.
EDIT: Ideal result of the transformation is the photo below:
I've just put any on the map argument like TIMEZONE_LIST.map((item: any)
Update: I've changed my object declaration type to
const TIMEZONE_NAME_MAP: Record<string, TimezoneListType> = {};
seems to work but I don't understand why? and Now the other part of my program is screaming as I've exported it to other places so I can use it like TIMEZONE_NAME_MAP.filter( ..so on) but does't recognize indexOf as I'm trying to use that inside the filter function. what do i do?
The assignment on the last line implies a for loop may be better suited to this scenario than a map. The purpose of the map is to create an array mapped to new values corresponding to the original, whereas your goal here is to create a dictionary.
PS, you also don’t seem to be using the values that you create during the mapping anywhere after assigning them (positive, minute)
.map operator returns one single object, no need to assign
const TIMEZONE_NAME_MAP: TimezoneListType[] = TIMEZONE_LIST.map((item) => {
const positive = item.offset >= 0;
const hour = item.offset | 0;
const minute = (item.offset - hour) * 60;
return {
...item,
lower: item.label.toLowerCase(),
offsetString: 'something',
};
});
Your getting that error because you are assigning an object to the array index and it is expecting you to an numeric index.
const TIMEZONE_MAP = TIMEZONE_LIST.map((item) => {
const positive = item.offset >= 0;
const hour = item.offset | 0;
const minute = (item.offset - hour) * 60;
return TIMEZONE_NAME_MAP[index] = { // typescript is screaming in this line.
...item,
lower: item.label.toLowerCase(),
offsetString: 'something',
};
});
EDIT: this would be a better way:
const TIMEZONE_MAP = TIMEZONE_LIST.map((item, index) => {
const positive = item.offset >= 0;
const hour = item.offset | 0;
const minute = (item.offset - hour) * 60;
return { // typescript is screaming in this line.
...item,
lower: item.label.toLowerCase(),
offsetString: 'something',
};
});
Edit 2 as per new requirement:
const TIMEZONE_MAP = TIMEZONE_LIST.map((item, index) => {
const positive = item.offset >= 0;
const hour = item.offset | 0;
const minute = (item.offset - hour) * 60;
let obj = {};
obj[item.name] = {...item,
lower: item.label.toLowerCase(),
offsetString: 'something'};
return obj;
});

How to get a leading '+' for positive numbers in Intl.NumberFormat?

I'm using Intl.NumberFormat to convert a number type to a formatted string in typescript/javascript in Angular2. I want a native solution and this is ideal, but I need a leading plus sign to be included for positive numbers.
If this is not possible with Intl.NumberFormat how else might I do it natively?
#Input() amount : number;
drawLabel() {
var formatter = new Intl.NumberFormat("en-GB",
{
style: "decimal",
minimumFractionDigits:1
});
...
this.label = formatter.format(this.amount)
}
In 2019 you do this:
var formatter = new Intl.NumberFormat("en-GB", { style: "decimal", signDisplay: 'always' });
console.log(formatter.format(-100.123456));
// output -100.123
console.log(formatter.format(100.123456));
// output +100.123
console.log(formatter.format(0));
// output +0.
Try something like this:
class FormatterWithSign extends Intl.NumberFormat {
constructor(...args) {
super(...args);
}
format(x) {
var res = super.format(x);
return x < 0 ? res : "+" + res;
}
}
var formatter = new FormatterWithSign("en-GB", { style: "decimal", minimumFractionDigits:1 });
console.log(formatter.format(-100.123456));
console.log(formatter.format(100.123456));
console.log(formatter.format(0));
Just check if this.amount is bigger than 0. Add a leading + if that is the case.
if(this.amount > 0) this.label = "+"+formatter.format(this.amount);
else this.label = formatter.format(this.amount);
better
this.label = formatter.format(this.amount);
if(this.amount > 0) this.label = "+"+this.label;
or in short
this.label = this.amount > 0 ? "+"+formatter.format(this.amount): formatter.format(this.amount)
You can also add Your own formatWithSign method :
Intl.NumberFormat.prototype.formatWithSign = function(x)
{
let y = this.format(x);
return x < 0 ? y : '+' + y;
}
const MyFormat = new Intl.NumberFormat('en-GB', { style: "decimal", minimumFractionDigits: 2, maximumFractionDigits:2} )
console.log(MyFormat.formatWithSign(-78.123456)); // -78,12
console.log(MyFormat.formatWithSign(90.123456)); // +90.12
console.log(MyFormat.formatWithSign(0)); // +0.00

Categories

Resources