Parcourir la source

:sparkles: saving one-true-pairing based on stable-marriage compromises

tags/0.0.1
j il y a 4 ans
Parent
révision
be4d5a949a
1 fichiers modifiés avec 137 ajouts et 20 suppressions
  1. 137
    20
      backend/db/survey-generator.js

+ 137
- 20
backend/db/survey-generator.js Voir le fichier

@@ -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
+}

Chargement…
Annuler
Enregistrer