-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathCode.gs
135 lines (113 loc) · 3.22 KB
/
Code.gs
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
const DUMMY_VERTEX = null
const source = SpreadsheetApp.getActive().getSheetByName('Form responses 1')
const target = SpreadsheetApp.getActive().getSheetByName('Schedule')
const index = () => {
const data = source.getDataRange().getDisplayValues()
const rows = data.slice(1)
const availabilities = getAvailabilities(rows)
const matches = hopcroftKarp(availabilities)
const result = Object.keys(matches).reduce((array, name) => {
return [...array, [name, matches[name]]]
}, [])
target.getRange(2, 1, rows.length, 2).setValues(result)
}
const getAvailabilities = (rows) => {
return rows.reduce((obj, row) => {
const name = row[1]
const availabilities = row[2].replaceAll(' ', '').split(',')
return { ...obj, [name]: availabilities }
}, {})
}
const reflect = (array) => {
return array.reduce((obj, value) => ({ ...obj, [value]: value }), {})
}
const createMatrix = (graph) => {
return Object.keys(graph).reduce(
(obj, name) => ({
...obj,
[name]: reflect(graph[name]),
}),
{}
)
}
const partitionMatrix = (matrix) => {
return {
names: reflect(Object.keys(matrix)),
times: reflect(
Object.keys(matrix)
.reduce((array, name) => [...array, ...Object.keys(matrix[name])], [])
.filter((v, i, array) => array.indexOf(v) === i)
),
}
}
function defaultMatch(partition) {
return {
names: Object.keys(partition.names).reduce(
(obj, name) => ({ ...obj, [name]: DUMMY_VERTEX }),
{}
),
times: Object.keys(partition.times).reduce(
(obj, time) => ({ ...obj, [time]: DUMMY_VERTEX }),
{}
),
}
}
const hopcroftKarp = (availabilities) => {
const distance = {}
const adjacency = createMatrix(availabilities)
const partition = partitionMatrix(adjacency)
const matches = defaultMatch(partition)
const breadthFirstSearch = () => {
const queue = []
for (const name in partition.names) {
if (name && matches.names[name] === DUMMY_VERTEX) {
distance[name] = 0
queue.push(name)
} else {
distance[name] = Infinity
}
}
distance[DUMMY_VERTEX] = Infinity
while (queue.length > 0) {
const name = queue.shift()
if (distance[name] < distance[DUMMY_VERTEX]) {
for (const time in adjacency[name]) {
if (
adjacency[name].hasOwnProperty(time) &&
distance[matches.times[time]] === Infinity
) {
distance[matches.times[time]] = distance[name] + 1
queue.push(matches.times[time])
}
}
}
}
return distance[DUMMY_VERTEX] !== Infinity
}
const depthFirstSearch = (name) => {
if (name === DUMMY_VERTEX) {
return true
}
for (const time in adjacency[name]) {
if (
time &&
distance[matches.times[time]] === distance[name] + 1 &&
depthFirstSearch(matches.times[time])
) {
matches.times[time] = name
matches.names[name] = time
return true
}
}
distance[name] = Infinity
return false
}
while (breadthFirstSearch()) {
for (const name in partition.names) {
if (name && matches.names[name] === DUMMY_VERTEX) {
depthFirstSearch(name)
}
}
}
return matches.names
}