@protonapp/stablematch
Version:
A pure javascript implementation of the Stable Matching Algorithm.
123 lines (113 loc) • 3.78 kB
JavaScript
const _ = require('underscore')
// -*- coding: utf-8 -*-
//-----------------------------------------------------------------------------
// file: $Id$
// desc: implements a solution to the stable matching problem using a
// left-optimized algorithm. shamelessly adapted from:
// https://github.com/paulgb/Python-Gale-Shapley/
// auth: metagriffin <mg.npmjs@uberdev.org>
// date: 2012/12/29
// copy: (C) CopyLoose 2012 UberDev <hardcore@uberdev.org>, No Rights Reserved.
//-----------------------------------------------------------------------------
// for node compatibility...
var exports = {};
//---------------------------------------------------------------------------
exports.match = function(A, B, rankA, rankB) {
if ( ! A || ! B || ! A.length || ! B.length )
return [];
if ( A.length == B.length )
return exports._match(A, B, rankA, rankB);
// TODO: this is a brute-force implementation of getting both
// lists to be of symmetric length... make this "better".
// for example, build this directly into _match() or use
// deterministic exclusion of the longer data set.
var sA = _.rest(A, 0);
var sB = _.rest(B, 0);
var mlen = Math.max(sA, sB);
while ( sA.length < mlen )
sA.push(null);
while ( sB.length < mlen )
sB.push(null);
var sRA = function(a) {
var ret = rankA(a);
while ( ret.length < mlen )
ret.push(null);
return ret;
};
var sRB = function(b) {
var ret = rankB(b);
while ( ret.length < mlen )
ret.push(null);
return ret;
};
var ret = exports._match(sA, sB, sRA, sRB);
return _.filter(ret, function(pair) {
return pair[0] != null && pair[1] != null;
});
};
//---------------------------------------------------------------------------
exports._match = function(A, B, rankA, rankB) {
// this translates sets A and B to indeces, since _imatch can only work
// with sets of elements that can be used as the key in a hash (in this
// implementation).
var iA = _.range(A.length);
var iB = _.range(B.length);
var iRA = function(ia) {
var ret = rankA(A[ia]);
return _.map(ret, function(item) {
return _.indexOf(B, item);
});
};
var iRB = function(ib) {
var ret = rankB(B[ib]);
return _.map(ret, function(item) {
return _.indexOf(A, item);
});
};
var ret = exports._imatch(iA, iB, iRA, iRB);
return _.map(ret, function(item) {
return [A[item[0]], B[item[1]]];
});
};
//---------------------------------------------------------------------------
exports._imatch = function(A, B, rankA, rankB) {
// TODO: improve this... it was a brute-force porting of
// https://github.com/paulgb/Python-Gale-Shapley
// without any eye on optimal outcome or performance...
//: `partners` is a paring hash of { a => [b, rank] }
var partners = {};
_.each(A, function(a) {
partners[a] = [rankA(a)[0], 0];
});
//: `stable` indicates stability of the current pairing in `partners`
var stable = false;
while ( ! stable )
{
stable = true;
_.each(B, function(b) {
var paired = false;
for ( var n=0 ; n<A.length ; n++ )
{
var a = rankB(b)[n];
var pair = partners[a];
if ( pair[0] == b )
{
if ( paired )
{
stable = false;
partners[a] = [rankA(a)[pair[1] + 1], pair[1] + 1];
}
else
paired = true;
}
}
});
}
return _.map(_.keys(partners), function(a) {
return [a, partners[a][0]];
});
};
module.exports = exports;
//-----------------------------------------------------------------------------
// end of $Id$
//-----------------------------------------------------------------------------