/* dict class
by Caio Chassot (http://v2studio.com/k/code/)

version 0.2, relatively unstested (call it alpha)

future: take muliple arguments in merge/update
    2004-08-29
        added each
        added renameKey
    2004-08-27
        initial release
*/

Dict.prototype = new Object();
Dict.prototype.constructor = Dict;

function Dict(d) {
    if (d) this.update(d);
}

function dict(d) { return new Dict(d) }

Dict.prototype.isProperty = function(p) {
    return isdef(this.constructor.prototype[p]);
}

Dict.prototype.update = function(d) {
    for (var k in d) if (!this.isProperty(k)) this[k] = d[k];
    return this;
}

Dict.prototype.merge = function(d) {
    return this.copy().update(d);
}

Dict.prototype.keys = function() {
    var ks = [];
    for (var k in this) if (!this.isProperty(k)) ks.push(k);
    return ks;
}

Dict.prototype.values = function() {
    var self = this;
    return map(this.keys(), function(k){return self[k]})
}

Dict.prototype.entries = function() {
    return combine(this.keys(), this.values(), null)
}
Dict.prototype.items = Dict.prototype.entries;
Dict.prototype.pairs = Dict.prototype.entries;

Dict.prototype.find = function(k,d) {
    return this.hasKey(k)? this[k] : d;
}

Dict.prototype.clear = function() {
    var self = this;
    each(this.keys(), function(k){ delete self[k] });
}

Dict.prototype.copy = function() {
    return new Dict(this);
}

Dict.prototype.hasKey = function(k) {
    return this.keys().has(k)
}

Dict.prototype.length = function() {
    return this.keys().length;
}

Dict.prototype.isEmpty = function() {
    return !this.length();
}

Dict.prototype.pop = function(k, d) {
    v = this.hasKey(k)? this[k] : d;
    delete this[k];
    return v;
}

Dict.prototype.popItem = function() {
    if (this.isEmpty()) return;
    var k = this.keys()[0];
    return [k, this.pop(k)]
}

Dict.prototype.renameKey = function(from, to) {
    this[to] = this.pop(from);
    return this
}

Dict.prototype.each = function(fn) {
    var self = this;
    if (!isFunction(fn)) fn = __strfn('k,v',fn);
    each(this.entries(), function(e){ fn(e[0],e[1]) });
}

Dict.prototype.reject = function(fn) {
    var self = this;
    if (!isFunction(fn)) fn = __strfn('k,v',fn);
    each(this.entries(), function(e){
        if (fn(e[0],e[1])) delete self[e[0]]
    });
    return self;
}

Dict.prototype.select = function(fn) {
    if (!isFunction(fn)) fn = __strfn('k,v',fn);
    return this.copy().reject(function(k,v){return !fn(k,v)});
}

Dict.prototype.toObject = function() {
    return this.entries().reduce({}, 'a[b[0]] = b[1]; a');
}

Dict.prototype.toString = function(){
    var k_len = map(this.keys(),'item.length').max();
    return map(this.entries(), function(e){
        return '{0} : {1}'.subn([e[0].toString().padRight(k_len), e[1]]) }).njoin();
}

Dict.fromPairs = function(pairs, fn) {
    if (fn) {
        if (!isFunction(fn)) fn = __strfn('k,v',fn);
        pairs = map(pairs, function(p){return fn(p[0],p[1])});
    }
    return pairs.reduce(new Dict, 'a[b[0]] = b[1]; a');
}

Dict.fromKeys = function(ks,v) {
    if (!isFunction(v)) v = function(){return v}
    return reduce(ks, new Dict, function(d,k){ d[k] = v(k); return d });
}

