|
|
@@ -69,12 +69,53 @@ class DummyProfile {
|
|
69
|
69
|
this.durationPref = null
|
|
70
|
70
|
this.locationPref = null
|
|
71
|
71
|
|
|
|
72
|
+ this.shouldDelete = false
|
|
|
73
|
+
|
|
72
|
74
|
// profile is constructed of 12 answers, 2 for each dimension
|
|
73
|
75
|
this.profileResponses = this.getRandomAnswers(12, importance)
|
|
74
|
76
|
this.langPref = this.getRandomAnswers(rand(4), langs)
|
|
75
|
77
|
this.durationPref = this.getRandomAnswers(1, duration)
|
|
76
|
78
|
this.locationPref = this.getRandomAnswers(1, location)
|
|
|
79
|
+
|
|
|
80
|
+ this.matchPref = null
|
|
|
81
|
+
|
|
|
82
|
+ this.candidateIndex = 0
|
|
|
83
|
+ this.otp = null
|
|
|
84
|
+ this.forder = []
|
|
|
85
|
+ }
|
|
|
86
|
+ clearPlaceholderMatches() {
|
|
|
87
|
+ this.matchPref = this.matchPref.filter(
|
|
|
88
|
+ match => match.shouldDelete == false,
|
|
|
89
|
+ )
|
|
|
90
|
+ this.forder = this.forder.filter(
|
|
|
91
|
+ potentialFiance => potentialFiance.shouldDelete == false,
|
|
|
92
|
+ )
|
|
|
93
|
+ if (this.otp.shouldDelete) {
|
|
|
94
|
+ this.otp = null
|
|
|
95
|
+ }
|
|
|
96
|
+ // console.log(this.matchPref)
|
|
|
97
|
+ }
|
|
|
98
|
+ rank(p) {
|
|
|
99
|
+ for (let i = 0; i < this.matchPref.length; i++)
|
|
|
100
|
+ if (this.matchPref[i] === p) return i
|
|
|
101
|
+ return this.matchPref.length + 1
|
|
|
102
|
+ }
|
|
|
103
|
+ prefers(p) {
|
|
|
104
|
+ return this.rank(p) < this.rank(this.otp)
|
|
77
|
105
|
}
|
|
|
106
|
+ nextCandidate() {
|
|
|
107
|
+ if (this.candidateIndex >= this.matchPref.length) return null
|
|
|
108
|
+ return this.matchPref[this.candidateIndex++]
|
|
|
109
|
+ }
|
|
|
110
|
+ engageTo(p) {
|
|
|
111
|
+ if (p.otp) p.otp.otp = null
|
|
|
112
|
+ p.otp = this
|
|
|
113
|
+ p.forder.unshift(this)
|
|
|
114
|
+ if (this.otp) this.otp.otp = null
|
|
|
115
|
+ this.otp = p
|
|
|
116
|
+ this.forder.unshift(p)
|
|
|
117
|
+ }
|
|
|
118
|
+ // My stuff
|
|
78
|
119
|
getRandomAnswers(count, options) {
|
|
79
|
120
|
const answers = []
|
|
80
|
121
|
for (let i = 0; i < count; i++) {
|
|
|
@@ -85,11 +126,25 @@ class DummyProfile {
|
|
85
|
126
|
}
|
|
86
|
127
|
}
|
|
87
|
128
|
|
|
88
|
|
-const generateDummyProfiles = count => {
|
|
|
129
|
+function engageEveryone(guys) {
|
|
|
130
|
+ var done
|
|
|
131
|
+ do {
|
|
|
132
|
+ done = true
|
|
|
133
|
+ guys.forEach(guy => {
|
|
|
134
|
+ if (!guy.otp) {
|
|
|
135
|
+ done = false
|
|
|
136
|
+ const gal = guy.nextCandidate()
|
|
|
137
|
+ if (!gal.otp || gal.prefers(guy)) guy.engageTo(gal)
|
|
|
138
|
+ }
|
|
|
139
|
+ })
|
|
|
140
|
+ } while (!done)
|
|
|
141
|
+}
|
|
|
142
|
+
|
|
|
143
|
+const generateDummyProfiles = (count, startFrom) => {
|
|
89
|
144
|
const profiles = []
|
|
90
|
145
|
for (let i = 0; i < count; i++) {
|
|
91
|
146
|
const dummyProfile = new DummyProfile()
|
|
92
|
|
- dummyProfile.id = i + 1
|
|
|
147
|
+ dummyProfile.id = startFrom + i + 1
|
|
93
|
148
|
profiles.push(dummyProfile)
|
|
94
|
149
|
}
|
|
95
|
150
|
profiles.forEach(dummy => {
|
|
|
@@ -105,8 +160,35 @@ const generateDummyProfiles = count => {
|
|
105
|
160
|
return profiles
|
|
106
|
161
|
}
|
|
107
|
162
|
|
|
108
|
|
-const generatedSeekers = generateDummyProfiles(100)
|
|
109
|
|
-const generatedProviders = generateDummyProfiles(10)
|
|
|
163
|
+const generatedSeekers = generateDummyProfiles(100, 0)
|
|
|
164
|
+const generatedProviders = generateDummyProfiles(80, generatedSeekers.length)
|
|
|
165
|
+const balanceSeekersAndProviders = (seekers, providers) => {
|
|
|
166
|
+ let diff = 0
|
|
|
167
|
+ let smallerList = null
|
|
|
168
|
+
|
|
|
169
|
+ if (seekers.length < providers.length) {
|
|
|
170
|
+ diff = providers.length - seekers.length
|
|
|
171
|
+ smallerList = seekers
|
|
|
172
|
+ } else {
|
|
|
173
|
+ diff = seekers.length - providers.length
|
|
|
174
|
+ smallerList = providers
|
|
|
175
|
+ }
|
|
|
176
|
+ let fillerId = seekers.length + providers.length
|
|
|
177
|
+ for (let i = 0; i < diff; i++) {
|
|
|
178
|
+ const filler = new DummyProfile()
|
|
|
179
|
+ filler.id = fillerId + i + 1
|
|
|
180
|
+ filler.profileResponses = filler.profileResponses.map(
|
|
|
181
|
+ (answer, response_key_id) => {
|
|
|
182
|
+ const answerObj = {}
|
|
|
183
|
+ // aka: id for the question we asked
|
|
|
184
|
+ answerObj[response_key_id] = answer
|
|
|
185
|
+ return answerObj
|
|
|
186
|
+ },
|
|
|
187
|
+ )
|
|
|
188
|
+ filler.shouldDelete = true
|
|
|
189
|
+ smallerList.push(filler)
|
|
|
190
|
+ }
|
|
|
191
|
+}
|
|
110
|
192
|
|
|
111
|
193
|
const scoreMatch = (seeker, potentialMatch) => {
|
|
112
|
194
|
const seekerResponseValues = seeker.profileResponses.map(res =>
|
|
|
@@ -120,24 +202,59 @@ const scoreMatch = (seeker, potentialMatch) => {
|
|
120
|
202
|
)
|
|
121
|
203
|
}
|
|
122
|
204
|
const compareProfile = (seeker, unorderedPotentialMatches) => {
|
|
123
|
|
- const scored = unorderedPotentialMatches.map(potentialMatch => {
|
|
124
|
|
- // add the match to object keyed by score
|
|
125
|
|
- return {
|
|
126
|
|
- profileMatchScore: scoreMatch(seeker, potentialMatch),
|
|
127
|
|
- profile: potentialMatch,
|
|
128
|
|
- }
|
|
129
|
|
- })
|
|
|
205
|
+ const scored = unorderedPotentialMatches
|
|
|
206
|
+ .map(potentialMatch => {
|
|
|
207
|
+ // add the match to object keyed by score
|
|
|
208
|
+ return {
|
|
|
209
|
+ profileMatchScore: scoreMatch(seeker, potentialMatch),
|
|
|
210
|
+ profile: potentialMatch,
|
|
|
211
|
+ }
|
|
|
212
|
+ })
|
|
|
213
|
+ .sort((a, b) => a.profileMatchScore - b.profileMatchScore)
|
|
130
|
214
|
// return ordered by score
|
|
131
|
|
- return scored.sort((a, b) => a.profileMatchScore - b.profileMatchScore)
|
|
|
215
|
+ return scored.map(profileScore => profileScore.profile)
|
|
132
|
216
|
}
|
|
133
|
217
|
|
|
|
218
|
+balanceSeekersAndProviders(generatedSeekers, generatedProviders)
|
|
|
219
|
+
|
|
|
220
|
+// Score
|
|
134
|
221
|
generatedSeekers.forEach(seeker => {
|
|
135
|
|
- const matchQueue = compareProfile(seeker, generatedProviders).map(
|
|
136
|
|
- provider => ({
|
|
137
|
|
- score: provider.profileMatchScore,
|
|
138
|
|
- profile_id: provider.profile.id,
|
|
139
|
|
- }),
|
|
140
|
|
- )
|
|
141
|
|
- console.log(`\n---| Results for job_seeker: ${seeker.id} |---`)
|
|
142
|
|
- console.log(matchQueue)
|
|
|
222
|
+ seeker.matchPref = compareProfile(seeker, generatedProviders)
|
|
|
223
|
+})
|
|
|
224
|
+generatedProviders.forEach(provider => {
|
|
|
225
|
+ provider.matchPref = compareProfile(provider, generatedSeekers)
|
|
143
|
226
|
})
|
|
|
227
|
+
|
|
|
228
|
+// Everything balanced ready for stable marriage
|
|
|
229
|
+if (generatedSeekers.length == generatedProviders.length) {
|
|
|
230
|
+ engageEveryone(generatedProviders)
|
|
|
231
|
+ generatedSeekers.forEach(seeker => seeker.clearPlaceholderMatches())
|
|
|
232
|
+ generatedProviders.forEach(provider => provider.clearPlaceholderMatches())
|
|
|
233
|
+
|
|
|
234
|
+ console.log('\nuser:', generatedProviders[0].id, '| provider')
|
|
|
235
|
+ console.log(
|
|
|
236
|
+ 'otp',
|
|
|
237
|
+ generatedProviders[0].otp ? generatedProviders[0].otp.id : null,
|
|
|
238
|
+ 'rank',
|
|
|
239
|
+ generatedProviders[0].otp
|
|
|
240
|
+ ? generatedProviders[0].matchPref
|
|
|
241
|
+ .map(m => m.id)
|
|
|
242
|
+ .indexOf(generatedProviders[0].otp.id) + 1
|
|
|
243
|
+ : generatedProviders[0].otp,
|
|
|
244
|
+ )
|
|
|
245
|
+ console.log(generatedProviders[0].matchPref.map(m => m.id))
|
|
|
246
|
+
|
|
|
247
|
+ console.log('---')
|
|
|
248
|
+ console.log('user:', generatedSeekers[0].id, '| seeker')
|
|
|
249
|
+ console.log(
|
|
|
250
|
+ 'otp',
|
|
|
251
|
+ generatedSeekers[0].otp ? generatedSeekers[0].otp.id : null,
|
|
|
252
|
+ 'rank',
|
|
|
253
|
+ generatedSeekers[0].otp
|
|
|
254
|
+ ? generatedSeekers[0].matchPref
|
|
|
255
|
+ .map(m => m.id)
|
|
|
256
|
+ .indexOf(generatedSeekers[0].otp.id) + 1
|
|
|
257
|
+ : generatedSeekers[0].otp,
|
|
|
258
|
+ )
|
|
|
259
|
+ console.log(generatedSeekers[0].matchPref.map(m => m.id))
|
|
|
260
|
+}
|