You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
254 lines
6.8 KiB
254 lines
6.8 KiB
module.exports = Hash;
|
|
var Traverse = require('traverse');
|
|
|
|
function Hash (hash, xs) {
|
|
if (Array.isArray(hash) && Array.isArray(xs)) {
|
|
var to = Math.min(hash.length, xs.length);
|
|
var acc = {};
|
|
for (var i = 0; i < to; i++) {
|
|
acc[hash[i]] = xs[i];
|
|
}
|
|
return Hash(acc);
|
|
}
|
|
|
|
if (hash === undefined) return Hash({});
|
|
|
|
var self = {
|
|
map : function (f) {
|
|
var acc = { __proto__ : hash.__proto__ };
|
|
Object.keys(hash).forEach(function (key) {
|
|
acc[key] = f.call(self, hash[key], key);
|
|
});
|
|
return Hash(acc);
|
|
},
|
|
forEach : function (f) {
|
|
Object.keys(hash).forEach(function (key) {
|
|
f.call(self, hash[key], key);
|
|
});
|
|
return self;
|
|
},
|
|
filter : function (f) {
|
|
var acc = { __proto__ : hash.__proto__ };
|
|
Object.keys(hash).forEach(function (key) {
|
|
if (f.call(self, hash[key], key)) {
|
|
acc[key] = hash[key];
|
|
}
|
|
});
|
|
return Hash(acc);
|
|
},
|
|
detect : function (f) {
|
|
for (var key in hash) {
|
|
if (f.call(self, hash[key], key)) {
|
|
return hash[key];
|
|
}
|
|
}
|
|
return undefined;
|
|
},
|
|
reduce : function (f, acc) {
|
|
var keys = Object.keys(hash);
|
|
if (acc === undefined) acc = keys.shift();
|
|
keys.forEach(function (key) {
|
|
acc = f.call(self, acc, hash[key], key);
|
|
});
|
|
return acc;
|
|
},
|
|
some : function (f) {
|
|
for (var key in hash) {
|
|
if (f.call(self, hash[key], key)) return true;
|
|
}
|
|
return false;
|
|
},
|
|
update : function (obj) {
|
|
if (arguments.length > 1) {
|
|
self.updateAll([].slice.call(arguments));
|
|
}
|
|
else {
|
|
Object.keys(obj).forEach(function (key) {
|
|
hash[key] = obj[key];
|
|
});
|
|
}
|
|
return self;
|
|
},
|
|
updateAll : function (xs) {
|
|
xs.filter(Boolean).forEach(function (x) {
|
|
self.update(x);
|
|
});
|
|
return self;
|
|
},
|
|
merge : function (obj) {
|
|
if (arguments.length > 1) {
|
|
return self.copy.updateAll([].slice.call(arguments));
|
|
}
|
|
else {
|
|
return self.copy.update(obj);
|
|
}
|
|
},
|
|
mergeAll : function (xs) {
|
|
return self.copy.updateAll(xs);
|
|
},
|
|
has : function (key) { // only operates on enumerables
|
|
return Array.isArray(key)
|
|
? key.every(function (k) { return self.has(k) })
|
|
: self.keys.indexOf(key.toString()) >= 0;
|
|
},
|
|
valuesAt : function (keys) {
|
|
return Array.isArray(keys)
|
|
? keys.map(function (key) { return hash[key] })
|
|
: hash[keys]
|
|
;
|
|
},
|
|
tap : function (f) {
|
|
f.call(self, hash);
|
|
return self;
|
|
},
|
|
extract : function (keys) {
|
|
var acc = {};
|
|
keys.forEach(function (key) {
|
|
acc[key] = hash[key];
|
|
});
|
|
return Hash(acc);
|
|
},
|
|
exclude : function (keys) {
|
|
return self.filter(function (_, key) {
|
|
return keys.indexOf(key) < 0
|
|
});
|
|
},
|
|
end : hash,
|
|
items : hash
|
|
};
|
|
|
|
var props = {
|
|
keys : function () { return Object.keys(hash) },
|
|
values : function () {
|
|
return Object.keys(hash).map(function (key) { return hash[key] });
|
|
},
|
|
compact : function () {
|
|
return self.filter(function (x) { return x !== undefined });
|
|
},
|
|
clone : function () { return Hash(Hash.clone(hash)) },
|
|
copy : function () { return Hash(Hash.copy(hash)) },
|
|
length : function () { return Object.keys(hash).length },
|
|
size : function () { return self.length }
|
|
};
|
|
|
|
if (Object.defineProperty) {
|
|
// es5-shim has an Object.defineProperty but it throws for getters
|
|
try {
|
|
for (var key in props) {
|
|
Object.defineProperty(self, key, { get : props[key] });
|
|
}
|
|
}
|
|
catch (err) {
|
|
for (var key in props) {
|
|
if (key !== 'clone' && key !== 'copy' && key !== 'compact') {
|
|
// ^ those keys use Hash() so can't call them without
|
|
// a stack overflow
|
|
self[key] = props[key]();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (self.__defineGetter__) {
|
|
for (var key in props) {
|
|
self.__defineGetter__(key, props[key]);
|
|
}
|
|
}
|
|
else {
|
|
// non-lazy version for browsers that suck >_<
|
|
for (var key in props) {
|
|
self[key] = props[key]();
|
|
}
|
|
}
|
|
|
|
return self;
|
|
};
|
|
|
|
// deep copy
|
|
Hash.clone = function (ref) {
|
|
return Traverse.clone(ref);
|
|
};
|
|
|
|
// shallow copy
|
|
Hash.copy = function (ref) {
|
|
var hash = { __proto__ : ref.__proto__ };
|
|
Object.keys(ref).forEach(function (key) {
|
|
hash[key] = ref[key];
|
|
});
|
|
return hash;
|
|
};
|
|
|
|
Hash.map = function (ref, f) {
|
|
return Hash(ref).map(f).items;
|
|
};
|
|
|
|
Hash.forEach = function (ref, f) {
|
|
Hash(ref).forEach(f);
|
|
};
|
|
|
|
Hash.filter = function (ref, f) {
|
|
return Hash(ref).filter(f).items;
|
|
};
|
|
|
|
Hash.detect = function (ref, f) {
|
|
return Hash(ref).detect(f);
|
|
};
|
|
|
|
Hash.reduce = function (ref, f, acc) {
|
|
return Hash(ref).reduce(f, acc);
|
|
};
|
|
|
|
Hash.some = function (ref, f) {
|
|
return Hash(ref).some(f);
|
|
};
|
|
|
|
Hash.update = function (a /*, b, c, ... */) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
var hash = Hash(a);
|
|
return hash.update.apply(hash, args).items;
|
|
};
|
|
|
|
Hash.merge = function (a /*, b, c, ... */) {
|
|
var args = Array.prototype.slice.call(arguments, 1);
|
|
var hash = Hash(a);
|
|
return hash.merge.apply(hash, args).items;
|
|
};
|
|
|
|
Hash.has = function (ref, key) {
|
|
return Hash(ref).has(key);
|
|
};
|
|
|
|
Hash.valuesAt = function (ref, keys) {
|
|
return Hash(ref).valuesAt(keys);
|
|
};
|
|
|
|
Hash.tap = function (ref, f) {
|
|
return Hash(ref).tap(f).items;
|
|
};
|
|
|
|
Hash.extract = function (ref, keys) {
|
|
return Hash(ref).extract(keys).items;
|
|
};
|
|
|
|
Hash.exclude = function (ref, keys) {
|
|
return Hash(ref).exclude(keys).items;
|
|
};
|
|
|
|
Hash.concat = function (xs) {
|
|
var hash = Hash({});
|
|
xs.forEach(function (x) { hash.update(x) });
|
|
return hash.items;
|
|
};
|
|
|
|
Hash.zip = function (xs, ys) {
|
|
return Hash(xs, ys).items;
|
|
};
|
|
|
|
// .length is already defined for function prototypes
|
|
Hash.size = function (ref) {
|
|
return Hash(ref).size;
|
|
};
|
|
|
|
Hash.compact = function (ref) {
|
|
return Hash(ref).compact.items;
|
|
};
|