UNPKG

eust-conciliation

Version:
78 lines (60 loc) 2.23 kB
import { Injectable } from '@severo-tech/injection-decorator'; import cache from 'node-cache-decorator'; export interface ICombinationsValue { id: string; value: number | string; } @Injectable() export class CombinationsUtil { @cache('CombinationsUtil.byValue', 60 * 60) public byValue<T extends ICombinationsValue>(list: T[], target: number, resolveIfDirectMatch = false): T[][] { const innerList = list .map((el: T): T & { value: number } => ({ ...el, value: +el.value })); if (resolveIfDirectMatch) { const directMatches = innerList.filter((el: T): boolean => el.value === target); if (directMatches.length) { return directMatches.map((el: T): T[] => [el]); } } const sortedList = innerList.sort((a: T, b: T): number => +b.value - +a.value); const combinations = this.findCombination(sortedList, target); return combinations.filter((el: T[]): boolean => el.length > 0); } private findCombination<T extends { value: number }>(list: T[], target: number): T[][] { let combinationsCount = 0; function innerFunction(current: T[], remaining: T[], remainingValue: number): T[][] { if (combinationsCount >= 100) { return []; } if (remainingValue === 0) { combinationsCount += 1; return [current]; } if (remainingValue < 0) { return []; } const innerRemaining = remaining.filter((el: T): boolean => el.value <= remainingValue); if (innerRemaining.length === 0) { return []; } const sumOfRemaining = innerRemaining.reduce((total: number, el: T): number => +(total + el.value).toFixed(2), 0); if (sumOfRemaining < remainingValue) { return []; } const invoice = innerRemaining[0]; const invoiceValue = invoice.value; const includeNote = innerFunction( [...current, invoice], innerRemaining.slice(1), +(remainingValue - invoiceValue).toFixed(2), ); const excludeNote = innerFunction( current, innerRemaining.slice(1), remainingValue, ); return [...includeNote, ...excludeNote]; } return innerFunction([], list, target); } }