-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathbags.js
111 lines (95 loc) · 2.45 KB
/
bags.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
class Luggage {
constructor(raw_rules) {
this.bags_lookup = this.parseToBagsLookup(raw_rules);
this.rules = this.parseToRulesLookup(raw_rules);
}
parseToBagsLookup(raw_rules) {
let bags_lookup = {};
for (let rule of raw_rules) {
let [parent, children] = rule.split(' bags contain ');
if (!bags_lookup[parent]) {
bags_lookup[parent] = new Bag({ name: parent });
}
if (children.includes('no other')) {
continue;
}
children = children.split(', ');
for (let child of children) {
let [, count, name] = /(\d+) (\w+ \w+) bag/.exec(child);
count = parseInt(count, 10);
let bag = bags_lookup[name];
if (!bag) {
bag = new Bag({ name });
bags_lookup[name] = bag;
}
bag.addParent(bags_lookup[parent]);
}
}
return bags_lookup;
}
parseToRulesLookup(raw_rules) {
let bags_lookup = {};
for (let rule of raw_rules) {
let [parent, children] = rule.split(' bags contain ');
if (!bags_lookup[parent]) {
bags_lookup[parent] = [];
}
if (children.includes('no other')) {
continue;
}
children = children.split(', ');
for (let child of children) {
let [, count, name] = /(\d+) (\w+ \w+) bag/.exec(child);
count = parseInt(count, 10);
bags_lookup[parent].push(new Bag({ name, count }));
}
}
return bags_lookup;
}
countChildrenInside(bag_name) {
if (!this.rules[bag_name]) {
throw new Error(`Invalid bag name: "${bag_name}"`);
}
let rules = this.rules[bag_name];
// Early escape, technically isn't necessary but provides base-case clarity on the recursion
if (!rules.length) {
return 0;
}
let children_count = 0;
for (let bag of rules) {
let { name, count } = bag;
// Add the one bag we are looking at now
children_count += count;
// Plus its children (will be 0 if the child contains no bags itself)
children_count += count * this.countChildrenInside(name);
}
return children_count;
}
}
class Bag {
constructor({ name, count }) {
this.name = name;
this.count = count;
this.parent_bags = [];
}
addParent(parent_bag) {
this.parent_bags.push(parent_bag);
}
countUniqueParents() {
let lookup = this._getUniqueAncestorsLookup({});
return Object.keys(lookup).length;
}
_getUniqueAncestorsLookup(lookup) {
for (let parent of this.parent_bags) {
lookup[parent.name] = parent;
if (parent.parent_bags.length) {
parent._getUniqueAncestorsLookup(lookup);
}
}
return lookup;
}
}
module.exports = {
Luggage,
Bag,
};