eft
eft

Reputation: 2611

How can I define a set of key/values in javascript and both lookup values and iterate the set in order?

I have a set of key/values eg orange=123, banana=4, apple=567. How can I store these key/values in a javascript object such that I can:

  1. Retrieve a value via lookup eg set["orange"] should return 123 and,
  2. Iterate over the set in the order in which the key/value pairs were added.

It seems that for 1. an object literal would be suitable but the iteration order is not guaranteed and that for 2. an array of key/value pairs (object literals) would provide an iteration order but not the ability to lookup the value based on a key.

@* Thanks for all the answers - Is this problem not a common one? Do libraries like jQuery not include support for this kind of type?

Upvotes: 2

Views: 10295

Answers (4)

KooiInc
KooiInc

Reputation: 122956

How about cooking your own list constructor?

function List(obj) {

  if (this instanceof List) {
   var t     = this,
       keys  = [];

    /* inititalize: add the properties of [obj] to the list, 
       and store the keys of [obj] in the private keys array */
    for (var l in obj) {
       keys.push(l);
       t[l] = obj[l];
    }
    /* public:
       add a property to the list 
    */
     t.add = 
         function(key, value) {
              t[key] = value;
              keys.push(key);
              return t; /* allows method chaining */
            };

    /* public:
       return raw or sorted list as string, separated by [separator] 
       Without [sort] the order of properties is the order in which 
       the properties are added to the list
    */
     t.iterate =
       function(sort,separator){
         separator = separator || '\n';
         var ret   = [],
             lkeys = sort ? keys.slice().sort() : keys;

         for (var i=0;i<lkeys.length;i++){
           ret.push(lkeys[i]+': '+t[lkeys[i]]);
         }
       return ret.join(separator);
      };

  } else if (obj && obj instanceof Object) {
     return new List(obj);

  } else if (arguments.length === 2) { 
     var a    = {};
     a[String(arguments[0])] = arguments[1];
     return new List(a);

  } else { return true; }

 /* the 'if (this instanceof List)' pattern makes
    the use of the 'new' operator obsolete. The 
    constructor also allows to be initialized with
    2 parameters => 'List(key,value)' 
 */
}

now you can have it raw (the order you added props is maintained) or sorted:

var myList = 
 List( { orange:123,
         banana:4,
         apple:567 }
     );
myList.add('peach',786);
alert(myList.iterate());
  /*=>output:
    orange: 123
    banana: 4
    apple: 567
    peach: 786
  */
or: alert(myList.iterate(1));
  /*=>output:
    apple: 567
    banana: 4
    orange: 123
    peach: 786
  */

Upvotes: 7

annakata
annakata

Reputation: 75862

A simple encapsulation:

function Dictionary(p)
{
    if(!this.Add)
    {
        Dictionary.prototype.Add = function(a)
        {
            if(!a.length) {a = [a];}
            for(var i = 0, n=a.length; i<n; i++)
            {
                for(x in a[i])
                {
                    this[x] = a[i][x];
                    this.keys.push(x);
                }
            }
        }
    }

    this.keys = [];

    if(typeof(p)!='undefined') {this.Add(p);}
}

var a = new Dictionary({'orange':123, 'banana':4, 'apple':567});

alert(a.keys);//returns [orange,banana,apple]

alert(a.keys[0]);//returns orange

alert(a.orange) //returns 123

a.Add({'mango':88}); //another way to add data

a.Add([{kiwi:16},{grapefruit:79}]) //another way to add data

alert(a.keys);//returns [orange,banana,apple,mango,kiwi,grapefruit]

Upvotes: 2

Christian C. Salvad&#243;
Christian C. Salvad&#243;

Reputation: 827792

You can do it in different ways:

var a = {'orange':123, 'banana':4, 'apple':567};

var b = new Object();
b['orange'] = 123;
b['banana'] = 4;
b['apple'] = 567;

var c = new Object();
c.orange = 123;
c.banana = 4;
c.apple = 567;

The syntax is different but internally the three declarations are equivalent.

Upvotes: 1

Kenan Banks
Kenan Banks

Reputation: 212088

Both of your assumptions are correct. Object literals are not guaranteed to return keys in any order.

The only way to do this without spinning your own type is to maintain an ordered list of keys:

var obj = {
    orange:123,
    banana:4,
    apple:567
}

var keys = ['orange', 'banana', 'apple'];

for (var i=0; i<keys.length; i++){
  value = obj[keys[i]]];
}

Ugly, I know.

Upvotes: 2

Related Questions