function insertBoxes(data){ if (data.length <= 1) return data[0] ? [data] : data; function flatMap(o){ return o.inside.length ? o.inside.reduce((p,b) => p.concat(flatMap(b).map(f => f.concat(o.id))),[]) : [[o.id]]; } function getBest(arr, r = []){ var i = arr.findIndex(a => a.every(i => !used[i])),len,tgt,bests,best; if (i === -1) return r; len = arr[i].length; tgt = arr.slice(i); bests = tgt.filter(a => a.length === len && a.every(x => !used[x])); best = bests.map((a,i) => [a.reduceRight((p,c) => p + boxes[c].x + boxes[c].y, 0), i]) .reduce((p,c) => c[0] < p[0] ? c : p,[Infinity,-1]); bests[best[1]].forEach(i => used[i] = true); r.push(bests[best[1]]); return getBest(tgt,r); } var boxes = data.map((e,i) => ({id:i, x:Math.max(e[0],e[1]), y:Math.min(e[0],e[1]), inside:[]})), used = Array(data.length).fill(false), interim = boxes.map((b,_,a) => { a.forEach(box => (bx > box.x && by > box.y) && b.inside.push(box)); return b; }) .map(b => flatMap(b)) .reduce((p,c) => p.concat(c)) .sort((a,b) => b.length-a.length); return getBest(interim).map(b => b.map(i => data[i])) .concat(insertBoxes(data.reduce((p,c,i) => used[i] ? p : (p.push(c),p) ,[]))); } var input = [[9, 15], [64, 20], [26, 46], [30, 51], [74, 76], [53, 20], [66, 92], [90, 71], [31, 93], [75, 59], [24, 39], [99, 40], [13, 56], [95, 50], [3, 82], [43, 68], [2, 50]]; result = [], ps = 0, pe = 0, ps = performance.now(); result = insertBoxes(input); pe = performance.now(); console.log("boxing", input.length, "boxes done in:", pe-ps,"msecs and all boxes fit in just", result.length,"boxes"); console.log(JSON.stringify(result));