[JS] Ergonomics for the Decimal type via the JavaScript bindings
- Dominant language
- TypeScript
- Stars
- 112
- Forks
- 23
- Avg merge
- 21h 18m
- Merged PRs (30d)
- 8
Description
### Describe the enhancement requested
I really love the Arrow project and have gotten an enormous amount of value from it, and I realize this is essentially a duplicate of apache/arrow-js#100 but it's been a couple of years and I just want to reiterate how difficult it is to properly handle values with the `Decimal` type from the JavaScript bindings. All you get is very low-level access to the underlying fixed-width buffer and you have to dig through the Arrow source to figure out how to do anything non-trivial with them. In my use-case all I need to do is accurately stringify them and have just had to fix the 3rd bug I've hit with my implementation (handling `scale=0`). Here is what I've arrived at, but I wish I had more confidence that there aren't more bugs lurking (there's no way it handles Decimal256 properly, but I've never encountered them in the wild):
```ts
import { util } from "apache-arrow";
import { trimEnd } from "lodash";
function formatDecimal(value: Uint32Array, scale: number): string {
const negative = isNegative(value);
const sign = negative ? "-" : "";
if (negative) {
negate(value);
}
const str = util.bignumToString(new util.BN(value)).padStart(scale, "0");
if (scale === 0) {
return `${sign}${str}`;
}
const wholePart = str.slice(0, -scale) || "0";
const decimalPart = trimEnd(str.slice(-scale), "0") || "0";
return `${sign}${wholePart}.${decimalPart}`;
}
const MAX_INT32 = 2 ** 31 - 1;
function isNegative(value: Uint32Array): boolean {
// https://github.com/apache/arrow/blob/3600bd802fbf5eb0c6e00bdae7939bcc3d631eb1/cpp/src/arrow/util/basic_decimal.h#L125
return value[value.length - 1] > MAX_INT32;
}
function negate(value: Uint32Array): void {
// https://github.com/apache/arrow/blob/3600bd802fbf5eb0c6e00bdae7939bcc3d631eb1/cpp/src/arrow/util/basic_decimal.cc#L1144
let carry = 1;
for (let i = 0; i < value.length; i++) {
const elem = value[i];
const updated = ~elem + carry;
value[i] = updated;
carry &= elem === 0 ? 1 : 0;
}
}
```
### Component(s)
JavaScript
Contributor guide
Assessment
This issue has not been assessed yet.