1.0.5 • Published 5 months ago

@gooddata/number-formatter v1.0.5

Weekly downloads
-
License
LicenseRef-LICENS...
Repository
github
Last release
5 months ago

GoodData Number Formatter in TypeScript

Overview

This is a TypeScript reimplementation of the original gdc-numberjs library. It mirrors the original's logic with some modifications and exceptions.

Key Features & Differences

Core Features

  • Includes all original features, excluding scientific formatting which was removed due to negligible usage.
  • Some noted differences are:
    • Rounding discrepancies from floating-point precision variances.
    • Different handling of \0 characters.
    • Bug-fixes from the original version.
  • Allows the option legacyCompatibility=false to switch off certain behaviors.

Supporting Features

  • Most additional features have been incorporated with a restructured API for better efficiency.
  • For example, stripColors is now represented as the FormatOptions#includeColors attribute.
  • FormatOptions#includeColors and Formatter#Resolve imitate their counterparts from gdc-numberjs. However, color coding has been altered to provide more information to the user.
  • Specific functions like excelEscape and specific format string translations have not been included on account of their specificity and potential complications.

Format String Syntax

The format strings bear significant resemblances to Excel's custom number format strings. However, there are some key differences and additional features. This section explains the format string syntax without assuming any prior knowledge of Excel's format strings.

Number Format

A number format is the basic unit of a format string. It's a Unicode string where certain types of placeholders can be applied. For example, "$ #,###.00" is a format string that displays the number 1 as "$ 1.00" and the number 1000 as "$ 1,000.00".

Here, we explore all the supported placeholders:

0 (Zero)

A zero is a digit placeholder. It displays insignificant zeroes if a number has fewer digits than there are zeroes in the format. This is applicable to both the integer and fractional parts of the number.

Consider these examples for a better understanding:

FormatNumberResult
00.00404.00
00.00-4-04.00
00.004.104.10
00.004040.00

# (Hash)

A hash is also a digit placeholder. The difference from the 0 placeholder is that it does not display insignificant zeroes. This is applicable to both the integer and fractional parts of the number.

Consider these examples for a better understanding:

FormatNumberResult
##.##44
##.##-4-4
##.##4.14.1
##.##4040
##.##40.140.1

? (Question Mark)

This is a digit placeholder. The difference from the 0 placeholder is that insignificant zeros will be formatted as spaces (' '). This can be useful to left-pad the whole part of the number and thus ensure that formatted results (strings) are of the same length and have decimal points aligned.

FormatNumberResult
?#.0044.00
?#.004040.00
?#.0040.1140.11

NOTE: This placeholder is not so useful to format the fraction part of the string. Check out the section about decimal precision and rounding to find out why.

, (Comma)

Displays thousands separators or scales number by a multiple of 1000. Indeed, the comma has two possible uses, depending on where you place it.

To format a number with a thousand separator, place a comma between any two digit placeholders. For instance 0,0, #,#, or #,0.

Other placements of the comma characters can be used to apply value scaling. The scaling factor will be 1000 to the power of the number of consecutive commas. For instance, the format #,,.# means scaling by 1000^2 = 1000000.

There are several subtleties to be aware of when it comes to scaling.

  • If the commas are followed by a digit placeholder, then the scaling factor will be 1000 to the power of the number of consecutive commas minus one. For instance, the format #,,# means scaling by 1000.

  • You can specify scaling on either the whole part or fraction part of the format string, or both. If you specify the scaling on both sides of the format string, the effect is cumulative. For instance, the format #,.#, means scaling by 1000^2 = 1000000.

  • If you specify scaling multiple times on one side of the format string, then the first-found occurrence will be used; all other occurrences will be ignored.

Let's see a few examples; please note that these are intentionally contrived to demonstrate the 'funny stuff' regarding the scaling specification. See recommendations below this table

FormatNumberResultNote
#,#12341,234Thousands separators
#,.00012341.234Scaling specified on the left side
#.000,12341.234Scaling specified on the right side
#,,#12341Scaling by 1000
,,#12341Scaling by 1000
,,#.0,12345671.2Scaling by 1000000
#,#,,#.012345671,234.6Separators and scaling by 1000

Recommendation: keep it simple. Try to avoid cases where commas to indicate scaling are followed by a digit placeholder. This unnecessarily complicates thinking about the format string. If possible, keep the scaling commas at the end of the format string, ideally at the end of the whole part of the string.

. (period)

A placeholder of the decimal point. If the format string does not specify decimal point, then all the numbers will be rounded and formatted as integers.

The fraction part of the format string should always start with a sequence of digit placeholder characters which indicates the desired precision to use for the fraction part. This may then be followed by any other characters.

NOTE: If you specify decimal point but the string after decimal point does not include any digit placeholders, then all characters to the right of the decimal point (including the decimal point) will be ignored during formatting.

Decimal precision, rounding and trimming zeroes

The number of consecutive digit placeholders that follow the decimal point will be used as decimal precision. For instance both #.0000 or #.#### indicate decimal precision 4. The formatter will round the number to the indicated precision. If the number is not at the desired precision, it will be expanded and padded with zeroes.

When it comes to substitution of digit placeholders, the logic is different when formatting the fraction part of the number. Most importantly, due to how precision is set and handled, the ? placeholder is effectively treated the same as the 0 placeholder.

The # placeholder is treated the same as when formatting the whole part - the trailing zeroes are trimmed. Furthermore, if the format string for the fraction part is composed fully of hashes and the formatted number's fraction part is zero, then all trailing zeroes are trimmed and the decimal point will not be printed at all.

FormatNumberResult
#4.14
#4.55
0.04.14.1
0.04.154.2
0.##4.14.1
0.##4.014.01
0.##4.0014
0.## USD4.0014 USD

Escaping

The format string can escape placeholder and thus indicate they should be treated literally. The escaping is done using backslash (\) in front of the placeholder.

Just keep in mind that if you are constructing the format string programmatically, you must escape the backslash as well (so \\).

Let's say you would like to treat comma in your format string literally and not as decimal point. Then you can code a format string \\. #.#.

Color Coding

The format strings may include color tags to indicate how the formatted results should be colored in contexts that allow this. There are three types of supported color tags:

  • named color tags such code as for instance [red]; the formatter recognizes several named colors that you can use here: black, blue, cyan, green, magenta, red, white, yellow, none

    The color name is checked case-insensitive. If you specify unknown color, then the tag is not treated as a named color tag but treated literally. It will appear in the formatted value

  • custom color code tags, coded as [color=#FF0000]

    The color code may or may not include hash and the casing does not matter.

  • custom background code tags, coded as [backgroundColor=#000000]

    The background code may or may not include hash and the casing does not matter.

Since named color tag and custom color code tag both describe the same thing - color of the formatted value, the renderers prefer value specified on the custom color tag.

The color tags can appear anywhere in the format string. If you specify multiple custom color or background color tags within the same string, the first-found is taken into account and other are discarded.

RECOMMENDATION: to keep format strings sane and easier to comprehend, it is best to concentrate color coding tags in one place, ideally at the start of the format string.

Conditional Format

So far, we have covered the elementary number format strings. The conditional formatting enables you to specify alternative format strings for different numbers or ranges of numbers.

NOTE: with conditional formatting, the alternative format will always be formatting the absolute value of the number. This allows you to fully customize representation of negative numbers.

Implicit Alternatives

In a conditional format, the alternative format strings are separated by a semicolon. The conditional format enables the specification of multiple implicit alternatives and/or explicit conditions.

An implicit conditional format can contain up to 3 alternatives separated by a semicolon (;):

  • The first format string is for values >= 0.
  • The second format string is for values < 0.
  • The third format string is for values = 0.

Example: good ##.##;bad ##.##;okay would format 1 as good 1, -1 as bad 1, and 0 as okay.

Explicit Alternatives

Besides implicit formats, explicit conditions and formats can be specified. The explicit condition specifies operator and right-hand-side value and then the format string. For instance, [=1000]#, grand would format number 1000 as 1 grand.

Supported operators are: =, <, >, <=, >=. The right hand side is a positive or negative integer or NULL. A NULL right-hand-side only makes sense with an equals operator. If you use it with any other operator, then that particular alternative will be ignored.

The null conditions are useful to provide special literals to use when the value to format is NULL (NULL as in not present at all, not null pointer). The format string provided in the null condition is always treated literally.

Combining Formats

Explicit and implicit alternatives can be combined.

Example: [=13]fun;[=666]more fun;[>=1000000]#,,.00M;[>=1000]##,k;##;-##.00;0000.

Condition Evaluation Order

The evaluation order of conditions is as follows:

  1. All equals conditions are evaluated first in a left to right order.
  2. All other conditions are evaluated in a left to right order.

Coloring

Format string for each alternative can include its own color coding.

Arithmetic Expression Format

Arithmetic expressions allow division and modulo operations on the value before formatting using the specified format string. They are useful for formatting values that represent durations.

The arithmetic expressions are enclosed in triple braces and contain divisor, modulator and the format string separated by pipes: {{{[division]|[modulo][.]|[format]}}}.

  • division can be any positive float number.
  • modulo can be any positive integer and will be used to float-point modulo the value to format. modulo can be followed by a comma (.) character. This indicates whether to floor the fraction part of the value to format or not.
  • format can be a number format or conditional format.

An arithmetic expression {{{3600|24|#}}} will first divide the value to format by 3600, then perform modulo 24 on the result and then use the # to format the value.

The format string can contain up to 256 arithmetic expressions.

Unicode Support

The format strings work well with Unicode characters. You can express your artistic side and create simple gauges through the use of conditional formats and specialized Unicode characters.

Please note that the formatter implementation included here utilizes Unicode characters from the private use area, starting from U+E000 and potentially reaching up to U+E2FF. Refrain from including these characters in your format strings to avoid unexpected results.

Illustrative Format Strings

The formats displayed in the previous examples demonstrate basic usage. Presented here are a few more complex examples. For the sake of brevity, color coding is not included.

Conditional Value Scale-Down

[=null]0; [>=1000000000]#,,,.0 B; [>=1000000]$#,,.0 M; [=0]0; [>0]$#,##0; [<=-1000000000]-#,,,.0 B; [<=-1000000]-#,,.0 M; [<0]-$#,##0

Percentage and Small Gauge Formatting for Numbers

00.0%[>=.9]██████████; 00.0%[>=.8]█████████░; 00.0%[>=.7]████████░░; 00.0%[>=.6]███████░░░; 00.0%[>=.5]██████░░░░; 00.0%[>=.4]█████░░░░░; 00.0%[>=.3]████░░░░░░; 00.0%[>=.2]███░░░░░░░; 00.0%[>=.1]██░░░░░░░░; #0.0%█░░░░░░░░░

1.0.5

5 months ago

1.0.4

8 months ago

1.0.3

8 months ago

1.0.2

9 months ago

1.0.1

9 months ago

1.0.0

9 months ago

0.1.0-beta.0

9 months ago