13

How can I do something like the following in JS? I would like to imitate .pop() on an object rather than an array.

var deck = { 
    'cardK' :'13',
    'cardQ' :'12',
    'cardAJ':'11'
};

var val = deck.pop();


console.log("Key" + val.key ); 
console.log("Value" + val.val ); 

It seems like it's not possible.

benomatis
  • 5,536
  • 7
  • 36
  • 59
Il Profeta Profeta
  • 312
  • 1
  • 4
  • 17

5 Answers5

17

.pop is only available on an array. In JavaScript, objects (which are essentially associative arrays) are not ordered like an array, so there is no .pop method.

You could use an array:

var deck = [
    { key: 'cardK', val: 13 },
    { key: 'cardQ', val: 12 },
    { key: 'cardAJ', val: 11 },
];

var val = deck.pop();
console.log('key: ' + val.key);
console.log('aa: ' + val.val);
Ethan Brown
  • 26,892
  • 4
  • 80
  • 92
4

As suggested by other answers, the best solution here might be to use an array of objects. However you could also create your own pop function that removes a key from an object, for example:

function pop(obj) {
    var key = Object.keys(obj).pop();
    var result = {key: key, val: obj[key]};
    delete obj[key];
    return result;
}

var val = pop(deck);

You could add a similar pop function to Object.prototype so that you could do deck.pop(), but I would strongly recommend against that type of design.

Andrew Clark
  • 202,379
  • 35
  • 273
  • 306
  • Why recommend against these structures? In some cases they provide the highest level of performance, for example when you need to know the value of cardQ `var x = deck['cardQ'];` is much faster than looping though an array looking into an object. – Brian McGinity Feb 20 '14 at 06:36
  • @BrianMcGinity It's bad practise in general and breaks encapsulation https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain#Bad_practice.3A_Extension_of_native_prototypes. Currently it only breaks `for ... in` as your Object.prototype.addedStuff will be listed unless you check `Object.hasOwnProperty.call(obj,key)` and some people argue that using `for ... in` is bad to begin with anyway (without checking hasOwnproperty) but it still breaks encapsulation. You are changing the JavaScript built in prototypes, they are not yours to change. – HMR Feb 20 '14 at 07:28
  • 1
    @HMR, I am saying it is ok to use the object array indexed by string, and in many cases favor it over an array of objects index by number. I did not mean to imply that redefining prototye pop() or any prototype is a good idea. – Brian McGinity Feb 20 '14 at 08:11
3

You are right, it's not possible. See objects as maps or hash tables, rather than "associative arrays". The properties don't have an order and thus a method such as .pop would not make sense (unless of course it would remove a random property, like Python's dictionaries).

If you want to to use .pop and val.key and val.val, you have to create an array of objects instead:

var deck = [
  {key: 'cardK', val: '13'},
  {key: 'cardQ', val: '12'},
  {key: 'cardAJ', val: '11'}
];
Felix Kling
  • 795,719
  • 175
  • 1,089
  • 1,143
1

As I'm sure you know, .pop is a prototypal Array method, so you can't use it with Javascript objects.

Calling .pop on an array will remove the last element from the array. However, there isn't a "last" key-value pair with objects, as their order is not ever guaranteed. Despite this, if you don't care about order, you could implement a .pop-like function for use with objects, though, again, it wouldn't remove and return the final key-value pair.

Something like this should do the trick:

function pop(obj) {
  for (var key in obj) {
    var val = obj[key];
    delete obj[key];
    return {
        'key'   : key,
        'val'   : val,
    };
  };
};

Combined with your code:

var val = pop(deck);
console.log('key: ' + val.key);
console.log('aa: ' + val.val);
Walter Roman
  • 4,621
  • 2
  • 32
  • 36
  • F.J. already provided a [similar answer](http://stackoverflow.com/a/21899957/218196) 20 min earlier. – Felix Kling Feb 20 '14 at 06:58
  • @FelixKling I would delete it if I did not think my answer to be more in depth. In my opinion, that's more valuable than [answering with "it's not possible" and providing a workaround that requires a different data structure](http://stackoverflow.com/a/21899878/2539700). – Walter Roman Feb 21 '14 at 20:19
  • Wouldn't this be dangerous? I do not think this is safe. While it may work in most cases, I don't think you can assume here that the first item the for-in returns will be the last item added to the array. You may want to implement a 'keyed' stack so you know the pop is working correctly. Pop should always remove and give you the last item added to the array. Easy when indexed by and int, but keys/hases are not done this way. – Quadrivium Jul 06 '15 at 21:00
1

When working with this structure, which can be thought of as an associative array, you need to use different techniques. Things like pop(), slice() and even .length will not work as they do with numeric keyed arrays.

I use string keyed object arrays when searching for the key/value pair needs to happen fast.

Here's a jsPef I just created which shows the benefit of your array structure:

http://jsperf.com/bmcgin-object-array-tests (keep in mind the performance goes way up as the array gets bigger)

Also keep in mind the value can be a number, a string, an array, a function, an object ect...

Brian McGinity
  • 5,777
  • 5
  • 36
  • 46