Skip to content

Commit f618b49

Browse files
feat(2020-day-07): bagception - count the number of child bags a bag has
1 parent 989d881 commit f618b49

File tree

1 file changed

+86
-2
lines changed

1 file changed

+86
-2
lines changed

2020/day-7/bagRules.js

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
const console = require('../helpers')
2+
13
const parseRule = (rule) => {
24
const result = {}
35
// Get the color of the outer bag
@@ -52,9 +54,91 @@ const findAllowedOuter = (rules, color) => {
5254
}
5355

5456
const countInner = (rules, color) => {
55-
return {
56-
[color]: 5
57+
// const children = {}
58+
/** checks if rule matches color */
59+
const matchesColor = ({ outer }) => outer === color
60+
/** checks if an object has keys */
61+
// const hasKeys = (obj) => (Object.keys(obj).length > 0)
62+
/** type-safe addition */
63+
const add = (a, b) => {
64+
a = (typeof a === 'number') ? a : 0
65+
b = (typeof b === 'number') ? b : 0
66+
return a + b
67+
}
68+
/** type-safe multiplication */
69+
const multiply = (a, b) => {
70+
a = (typeof a === 'number') ? a : 1
71+
b = (typeof b === 'number') ? b : 1
72+
return a * b
73+
}
74+
/** multiply all keys in an object by a value */
75+
const multiplyObjKeys = (object, value) => {
76+
for (const key in object) {
77+
object[key] = multiply(object[key], value)
78+
}
5779
}
80+
/** combine two objects using the specified operator method for collsions */
81+
const combineObjects = (a = {}, b = {}, operator) => {
82+
const c = {}
83+
// check for collisions between fields across the objects and run operator() on them
84+
for (const [key, value] of Object.entries(b)) {
85+
c[key] = operator(a[key], value)
86+
}
87+
return Object.assign({}, a, c) // b not needed because covered in collision resolver
88+
}
89+
90+
console.debug('matching', color)
91+
92+
// Loop through the rules to find first level children
93+
const children = rules.filter(matchesColor) // find all matches for the color
94+
.filter(({ inner }) => (inner) && inner.length > 0) // filter for matches that have children
95+
.map(({ inner }) => {
96+
const res = {}
97+
// Convert into structured list
98+
inner.forEach(({ color, count }) => {
99+
res[color] = count
100+
})
101+
return res
102+
})
103+
.reduce((res, match) => {
104+
return combineObjects(res, match, add)
105+
}, {})
106+
107+
console.debug('First level match found', children)
108+
console.debug(`check the children of ${Object.keys(children).length} immediate child bags recursively`)
109+
// Take the list immediate children, and recursively tally the children in each
110+
const childResults = Object.entries(children)
111+
.map(([color, count]) => {
112+
// find the child's children
113+
const childChildren = countInner(rules, color)
114+
// multilply each of the child's children by the child amount
115+
multiplyObjKeys(childChildren, count)
116+
return childChildren
117+
})
118+
// .filter(hasKeys) // Drop empties
119+
.reduce((res, matches, idx) => {
120+
console.debug(matches)
121+
console.debug(
122+
'----------------------\n',
123+
`${idx}: child result ${color}:`, '\n',
124+
combineObjects(res, matches, add), '\n',
125+
'----------------------\n'
126+
)
127+
return combineObjects(res, matches, add)
128+
}, {})
129+
130+
console.debug(
131+
'---------------------\n',
132+
'results with children\n',
133+
'children:', children, '\n',
134+
'childResults:', childResults, '\n',
135+
'combined:',
136+
combineObjects(children, childResults, multiply), '\n',
137+
'---------------------\n\n'
138+
)
139+
140+
// Multiply the child results with the current level
141+
return combineObjects(children, childResults, multiply)
58142
}
59143

60144
module.exports = {

0 commit comments

Comments
 (0)