I'm trying to transform a flat list of account balances to a list of hierarchical account trees based on a list of unique codes. I'm struggling with how to solve this as I can't see what steps to take.
If you think of an excel sheet then the account balance would be a row and the codes field denotes the columns for how many levels the row has been broken down into. This effectively means that the the first codes[0] is the first level and codes[1] would be the next so on and so forth. By transforming the account balances to an account tree I want to sum the amountOut correctly for each level (codeType and code) and then add it as an account tree in the subAccounts array.
Below I've added all the types used as well as provided an example of the input and output.
interface AccountBalance {
id: string
codes: Code[]
amountOut: number
}
type Code = {
id: string
codeType: CodeTypes
code: string
description: string
}
enum CodeTypes {
account = "account",
responsible = "responsible",
project = "project",
object = "object",
counterPart = "counterPart",
free = "free",
}
type AccountTree = {
id: string
name: string
code: string
amountOut: number
subAccounts: AccountTree[]
}
Input example:
const accountBalances: AccountBalance[] = [
{
id: "671769fbd36fcd6c2c7f2d9b",
codes: [
{
id: "671769fbd36fcd6c2c7f2c2d",
codeType: codeTypeEnum.account,
code: "1250",
description: "Column A",
},
{
id: "671769fbd36fcd6c2c7f2bd5",
codeType: codeTypeEnum.responsible,
code: "17",
description: "Column B",
},
{
id: "671769fbd36fcd6c2c7f2bf7",
codeType: codeTypeEnum.counterPart,
code: "20",
description: "Column C",
},
],
amountOut: 24510549,
},
{
id: "671769fbd36fcd6c2c7f2d9c",
codes: [
{
id: "671769fbd36fcd6c2c7f2c2d",
codeType: codeTypeEnum.account,
code: "1250",
description: "Column A",
},
{
id: "671769fbd36fcd6c2c7f2bee",
codeType: codeTypeEnum.responsible,
code: "40",
description: "Column B",
},
{
id: "671769fbd36fcd6c2c7f2c08",
codeType: codeTypeEnum.counterPart,
code: "S3",
description: "Column C",
},
],
amountOut: 0,
},
{
id: "671769fbd36fcd6c2c7f2d9d",
codes: [
{
id: "671769fbd36fcd6c2c7f2c2d",
codeType: codeTypeEnum.account,
code: "1250",
description: "Column A",
},
{
id: "671769fbd36fcd6c2c7f2bdb",
codeType: codeTypeEnum.responsible,
code: "80",
description: "Column B",
},
{
id: "671769fbd36fcd6c2c7f2bdc",
codeType: codeTypeEnum.counterPart,
code: "52",
description: "Column C",
},
],
amountOut: 6381398,
},
]
Output I want, example:
const expected: AccountTree = {
id: "671769fbd36fcd6c2c7f2c2d",
name: "Column A",
code: "1250",
amountOut: 30891947,
subAccounts: [
{
id: "671769fbd36fcd6c2c7f2bd5",
name: "Column B",
code: "17",
amountOut: 24510549,
subAccounts: [
{
id: "671769fbd36fcd6c2c7f2bf7",
name: "Column C",
code: "20",
amountOut: 24510549,
subAccounts: [],
},
],
},
{
id: "671769fbd36fcd6c2c7f2bee",
name: "Column B",
code: "40",
amountOut: 0,
subAccounts: [
{
id: "671769fbd36fcd6c2c7f2c08",
name: "Column C",
code: "S3",
amountOut: 0,
subAccounts: [],
},
],
},
{
id: "671769fbd36fcd6c2c7f2bdb",
name: "Column B",
code: "80",
amountOut: 6381398,
subAccounts: [
{
id: "671769fbd36fcd6c2c7f2bdc",
name: "Column C",
code: "52",
amountOut: 6381398,
subAccounts: [],
},
],
},
],
}
What I've gotten so far is this
const uniqueCodeTypes = [
...new Set(
accountBalances.reduce<CodeTypes[]>(
(acc, balance) => [
...acc,
...balance.codes.flatMap((code) => code.codeType),
],
[],
),
),
]
const uniqueCodesForCodeType = uniqueCodeTypes.map((codeType) => {
const uniqueCodesForCodeType = [
...new Map(
accountBalances.reduce<Code[]>((acc, balance) => {
const code = balance.codes.find(
(code) => code.codeType === codeType,
)
return code ? [...acc, code] : acc
}, []).map((code) => [code.code, code])
).values(),
]
return uniqueCodesForCodeType
})
But I'm unsure of how to proceed after this.
In plain Javascript, you could search for id
at the same level and add object if not exists.
const
codeTypeEnum = { account: "account", responsible: "responsible", project: "project", object: "object", counterPart: "counterPart", free: "free"},
accountBalances = [{ id: "671769fbd36fcd6c2c7f2d9b", codes: [{ id: "671769fbd36fcd6c2c7f2c2d", codeType: codeTypeEnum.account, code: "1250", description: "Column A" }, { id: "671769fbd36fcd6c2c7f2bd5", codeType: codeTypeEnum.responsible, code: "17", description: "Column B" }, { id: "671769fbd36fcd6c2c7f2bf7", codeType: codeTypeEnum.counterPart, code: "20", description: "Column C" }], amountOut: 24510549 }, { id: "671769fbd36fcd6c2c7f2d9c", codes: [{ id: "671769fbd36fcd6c2c7f2c2d", codeType: codeTypeEnum.account, code: "1250", description: "Column A" }, { id: "671769fbd36fcd6c2c7f2bee", codeType: codeTypeEnum.responsible, code: "40", description: "Column B" }, { id: "671769fbd36fcd6c2c7f2c08", codeType: codeTypeEnum.counterPart, code: "S3", description: "Column C" }], amountOut: 0 }, { id: "671769fbd36fcd6c2c7f2d9d", codes: [{ id: "671769fbd36fcd6c2c7f2c2d", codeType: codeTypeEnum.account, code: "1250", description: "Column A" }, { id: "671769fbd36fcd6c2c7f2bdb", codeType: codeTypeEnum.responsible, code: "80", description: "Column B" }, { id: "671769fbd36fcd6c2c7f2bdc", codeType: codeTypeEnum.counterPart, code: "52", description: "Column C" }], amountOut: 6381398 }],
result = accountBalances.reduce((r, { amountOut, codes }) => {
codes.reduce((level, o) => {
let target = level.find(({ id, codeType }) => id === o.id && codeType === o.codeType);
if (!target) {
target = { ...o, amountOut: 0, subAccounts: [] };
level.push(target);
}
target.amountOut += amountOut;
return target.subAccounts;
}, r);
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }