原來的需求是這樣的,用戶生成了400元訂單,須要400元的發票去抵消,發票數和訂單數不受限制,this
將問題擬物化,訂單能夠想象成杯子,發票能夠想象成水,如今要把水分配到每一個杯子中去
咱們先記錄每一個杯子的起始值和結束值,還有當前值,起始值(start)=當前杯子的起始值加上上一個杯子的結束值,結束值(end)即起始值加上杯子的容量,當前值(index)即當前杯子中的水,若是大於杯子的容量則倒下一個杯子,依次循環,因此其實整個倒水能夠看作一個大杯子,按階段倒水,每次記錄倒水的index(即當前(或者當次)杯子中的水便可)spa
如今假設a杯50L, b杯20L
假如第一次倒, a[0, 50]杯子倒滿,剩餘20;index = 50 , start = 0 , end = 50;
第二次倒b杯[50, 70], 首先拿上一下剩餘的20L, 恰好倒滿,index = 20; start = 50 ,end = 70
依次類推code
話很少說,上代碼rem
export function getItemDistribution(a, b, waterKey, cupKey, policyId) { class ItemDistribution { cupList = []; result = []; constructor(list, maps) { this.waterKey = waterKey; this.cupKey = cupKey; this.policyId = policyId; this.sort(list, maps); this.init(list); } sort(list, maps) { let {waterKey, cupKey} = this; if (waterKey) { list = list.sort((a, b) => a[waterKey] - b[waterKey]); } else { list = list.sort((a, b) => a - b); } if (cupKey) { maps = maps.sort((a, b) => a[cupKey] - b[cupKey]); } else { maps = maps.sort((a, b) => a - b); } this.list = list; this.maps = maps; return {list, maps} } init(list) { if (list == null || list.length <= 0) { return; } let start = 0, end = 0, index = 0; let {waterKey} = this; list.forEach((item, id) => { if (!waterKey) { this.result[id] = []; } let water = waterKey ? item[waterKey] : item; end = start + Number(water); this.entry({start, index, end}); start = end; }); this.dispatchMaps(); } entry({start, index, end}) { index = start; this.cupList.push({start, end, index}) } dispatchMaps() { function isEmpty(o) { switch (JSON.stringify(o)) { case 'null': case undefined: return true; case '[]': return true; } return false; } let {maps = [], cupList, cupKey} = this; if (isEmpty(cupList) || isEmpty(maps)) return false; maps.forEach((item, id) => { let cup = cupKey ? item[cupKey] : item; this.dispatchCup({item: cup, id}); }); } getCup() { function isEqual(a, b) { return String(a.toFixed(2)) !== String(b.toFixed(2)); } let {cupList} = this; for (let i = 0; i < cupList.length; i++) { let item = cupList[i]; if (isEqual(item.end, item.index)) { return {cup: item, cupIndex: i}; } } return {}; } dispatchCup({item, id}) { let {cup, cupIndex} = this.getCup(); if (!cup) return false; let {index, end} = cup; let remain = end - index; let {waterKey, list, policyId, maps} = this; if (remain > item) { cup.index = cup.index + item; waterKey ? this.result.push({ ...list[cupIndex], [waterKey]: item, [policyId]: maps[id][policyId] }) : this.result[id].push(item); return false; } else { cup.index = cup.end; waterKey ? this.result.push({ ...list[cupIndex], [waterKey]: remain, [policyId]: maps[id][policyId] }) : this.result[id].push(remain); item -= remain; this.dispatchCup({item, id}) } } } return new ItemDistribution(a, b); }