Skip to content

Commit

Permalink
Merge branch 'release/v0.18.6'
Browse files Browse the repository at this point in the history
  • Loading branch information
holtwick committed Mar 28, 2024
2 parents d19cb4f + a043d63 commit 52cc483
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 3 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "zeed",
"type": "module",
"version": "0.18.5",
"version": "0.18.6",
"description": "🌱 Simple foundation library",
"author": {
"name": "Dirk Holtwick",
Expand Down Expand Up @@ -65,7 +65,7 @@
"watch": "nr build -- --watch src"
},
"devDependencies": {
"@antfu/eslint-config": "^2.9.0",
"@antfu/eslint-config": "^2.11.4",
"@antfu/ni": "^0.21.12",
"@types/node": "^20.11.30",
"@vitest/coverage-v8": "^1.4.0",
Expand Down
10 changes: 10 additions & 0 deletions src/common/data/object.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ describe('objectPlain', () => {
fn.prototype.x = 2
fn.z = 6

class Klass {
name = 'test'
}

const obj = {
a: 1,
b: {
Expand All @@ -198,6 +202,8 @@ describe('objectPlain', () => {
uint16: new Uint16Array([1, 2, 3]),
c: 2,
d: [3, 4, 5],
Klass,
newKlass: new Klass(),
},
}
const result = objectPlain(obj, {
Expand All @@ -209,6 +215,7 @@ describe('objectPlain', () => {
Object {
"a": 1,
"b": Object {
"Klass": Object {},
"c": 2,
"d": Array [
3,
Expand All @@ -226,6 +233,9 @@ describe('objectPlain', () => {
"true": "bool",
},
"nan": NaN,
"newKlass": Object {
"name": "test",
},
"rx": "/.*?.test/gim",
"set": Array [
1,
Expand Down
3 changes: 2 additions & 1 deletion src/common/data/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@ export function objectPlain(obj: any, opt?: {

// if (isObject(obj) || isFunction(obj)) {
const nobj: any = {}
for (const [key, value] of Object.entries(obj)) {
for (const key in obj) {
const value = obj[key]
if (filter(value))
nobj[key] = handleObject(value, depth + 1)
}
Expand Down
63 changes: 63 additions & 0 deletions src/common/data/rounding.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { arraySum } from './array'
import {
roundArrayOfNumbersToMatchSum,
roundArrayOfObjectsToMatchSum,
roundDown,
roundHalfAwayFromZero,
roundHalfDown,
Expand Down Expand Up @@ -181,4 +184,64 @@ describe('currency', () => {
expect(roundUp(-1.6)).toBe(-1)
})
})

it('distribute percentages', () => {
const values = [13.626332, 47.989636, 9.596008, 28.788024]

const objs = [{
name: '13',
percent: 13.626332,
}, {
name: '47',
percent: 47.989636,
}, {
name: '9',
percent: 9.596008,
}, {
name: '28',
percent: 28.788024,
}]

expect(arraySum(roundArrayOfNumbersToMatchSum(values))).toEqual(100)
expect(arraySum(roundArrayOfNumbersToMatchSum(values, 95, 2))).toEqual(95)

expect(roundArrayOfNumbersToMatchSum(values)).toMatchInlineSnapshot(`
Array [
14,
48,
9,
29,
]
`)

expect(roundArrayOfNumbersToMatchSum(values, 95, 2)).toMatchInlineSnapshot(`
Array [
12.94,
45.59,
9.12,
27.35,
]
`)

expect(roundArrayOfObjectsToMatchSum(objs, 'percent')).toMatchInlineSnapshot(`
Array [
Object {
"name": "13",
"percent": 14,
},
Object {
"name": "47",
"percent": 48,
},
Object {
"name": "9",
"percent": 9,
},
Object {
"name": "28",
"percent": 29,
},
]
`)
})
})
68 changes: 68 additions & 0 deletions src/common/data/rounding.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// From https://v2.dinerojs.com/docs/api/formatting/to-unit MIT

import { arraySum } from './array'

export type RoundingMode = (value: number) => number

export function isHalf(value: number) {
Expand Down Expand Up @@ -58,3 +60,69 @@ export const roundHalfTowardsZero: RoundingMode = value =>
isHalf(value)
? Math.sign(value) * Math.floor(Math.abs(value))
: Math.round(value)

/**
* This is useful for percentages that should sum up to 100.
* But can also be fine tuned.
*
* Original from https://github.com/super-ienien/percent-round
*/
export function roundArrayOfNumbersToMatchSum(ipt: number[], max = 100, decimalPlaces = 0) {
const iptPercents: number[] = [...ipt]
const length = ipt.length
const out: number[] = []
out.fill(0, length)

const total = arraySum(iptPercents)

if (total !== 0) {
const powDecimalPlaces = 10 ** decimalPlaces
const pow100 = max * powDecimalPlaces
let check100 = 0
for (let i = length - 1; i >= 0; i--) {
iptPercents[i] = max * iptPercents[i] / total
check100 += out[i] = Math.round(iptPercents[i] * powDecimalPlaces)
}

if (check100 !== pow100) {
const totalDiff = (check100 - pow100)
const roundGrain = 1
let grainCount = Math.abs(totalDiff)
const diffs = iptPercents.map((_, i) => Math.abs(out[i] - iptPercents[i] * powDecimalPlaces))

while (grainCount > 0) {
let idx = 0
let maxDiff = diffs[0]
for (let i = 1; i < length; i++) {
if (maxDiff < diffs[i]) {
// avoid negative result
if (check100 > pow100 && out[i] - roundGrain < 0)
continue
idx = i
maxDiff = diffs[i]
}
}
if (check100 > pow100)
out[idx] -= roundGrain
else
out[idx] += roundGrain

diffs[idx] -= roundGrain
grainCount--
}
}

if (powDecimalPlaces > 1)
return out.map(n => +((n / powDecimalPlaces).toFixed(decimalPlaces)))
}

return out
}

export function roundArrayOfObjectsToMatchSum<T extends Record<string, any>>(arr: T[], name: string, max = 100, decimalPlaces = 0): T[] {
return roundArrayOfNumbersToMatchSum(arr.map(o => o[name]), max, decimalPlaces)
.map((o, i) => ({
...arr[i],
[name]: o,
}))
}

0 comments on commit 52cc483

Please sign in to comment.