Ordered hash in JavaScript

前端 未结 9 997
心在旅途
心在旅途 2020-12-05 17:34

JavaScript objects have no order stored for properties (according to the spec). Firefox seems to preserve the order of definition of properties when using a for...in<

相关标签:
9条回答
  • 2020-12-05 17:55

    This question come up as the top search result. After not finding a ordered hash, i just wrote this small coffescript. Hopefully this will help folks landing on this page:

    ## OrderedHash
    # f = new OrderedHash
    # f.push('a', 1)
    # f.keys()
    # 
    class OrderedHash
     constructor: ->
       @m_keys = []
       @m_vals = {}
    
      push: (k,v) ->
        if not @m_vals[k]
          @m_keys.push k
        @m_vals[k] = v
    
      length: () -> return @m_keys.length
    
      keys: () -> return @m_keys
    
      val: (k) -> return @m_vals[k]
      vals: () -> return @m_vals
    
    0 讨论(0)
  • 2020-12-05 17:56

    No, since the Object type is specified to be an unordered collection of properties, you can not rely on that. (Or: You can only rely on that an object is an unordered collection of properties.)

    If you want to have an ordered hash set, you will need to implement it on your own.

    0 讨论(0)
  • 2020-12-05 17:59

    Realize its late but I needed this and couldn't find it elsewhere. *UPDATE Added necessary non-enumerable methods and properties. Quick ES 5 implementation (polyfill as needed):

    function orderedHash(object) {
        'use strict'
        var obj = object || {}
        Object.defineProperties(this, {
            'length': {
                value: 0,
                writable: true
            },
            'keys' : {
                value: [],
                writable: true
            },
            'sortedBy': {
                value: '',
                writable: true
            }
        })
        this.hash(obj)
        obj = null
    }
    Object.defineProperties(orderedHash.prototype, {
        'sortByKeys': {
            value: function sortByKeys() {
                var i, len, name
                this.keys.sort(function(a, b) {   
                    return a >= b ? 1 : -1
                })
                for (i=0, len = this.keys.length; i < len; ++i) {
                    name = this.keys[i]
                    this[i] = this[name]
                }
                this.sortedBy = 'keys'
                return null
            }   
        },
        'sortByValues': {
            value: function sortByValues() {
                var i, len, newIndex, name, ordered = [], names = this.keys.splice(0)
                this.keys = []
                for (i=0, len = this.length; i < len; ++i) {
                    ordered.push(this[i])
                    ordered.sort(function(a, b) {   
                        return a >= b ? 1 : -1
                    })
                    newIndex = ordered.lastIndexOf(this[i])
                    name = names[i]
                    this.keys.splice(newIndex, 0 , name)
                }
                for (i=0, len = ordered.length; i < len; ++i) {
                    this[i] = ordered[i]
                }
                this.sortedBy = 'values'
                return null
            }
        },
        'insert': {
            value: function insert(name, val) {
                this[this.length] = val
                this.length += 1
                this.keys.push(name)
                Object.defineProperty(this, name, {
                    value: val,
                    writable: true,
                    configurable: true
                })
                if (this.sortedBy == 'keys') {
                    this.sortByKeys()
                } else {
                    this.sortByValues()
                }
                return null
            }
        },
        'remove': {
            value: function remove(name) {
                var keys, index, i, len
                delete this[name]
                index = this.keys[name]
                this.keys.splice(index, 1)
                keys = Object.keys(this)
                keys.sort(function(a, b) {   
                    return a >= b ? 1 : -1
                })
                for (i=0, len = this.length; i < len; ++i) {
                    if (i >= index) {
                        this[i] = this[i + 1]
                    }
                }
                delete this[this.length - 1]
                this.length -= 1
                return null
            }
        },
        'toString': {
            value: function toString() {
                var i, len, string = ""
                for (i=0, len = this.length; i < len; ++i) {
                    string += this.keys[i]
                    string += ':'
                    string += this[i].toString()
                    if (!(i == len - 1)) {
                        string += ', '
                    }
                }
                return string
            }
        },
        'toArray': {
            value: function toArray() {
                var i, len, arr = []
                for (i=0, len = this.length; i < len; ++i) {
                    arr.push(this[i])
                }
                return arr
            }
        },
        'getKeys': {
            value: function getKeys() {
                return this.keys.splice(0)
            }
        },
        'hash': {
            value: function hash(obj) {
                var i, len, keys, name, val
                keys = Object.keys(obj)
                for (i=0, len = keys.length; i < len; ++i) {
                    name = keys[i]
                    val = obj[name]
                    this[this.length] = val
                    this.length += 1
                    this.keys.push(name)
                    Object.defineProperty(this, name, {
                        value: val,
                        writable: true,
                        configurable: true
                    })
                }
                 if (this.sortedBy == 'keys') {
                    this.sortByKeys()
                } else {
                    this.sortByValues()
                }
                return null
            }
        }
    })
    

    What happens here is that by using Object.defineProperty() instead of assignment can we make the properties non-enumerable, so when we iterate over the hash using for...in or Object.keys() we only get the ordered values but if we check hash.propertyname it will be there. There are methods provided for insertion, removal, assimilating other objects (hash()), sorting by key, sorting by value, converting to array or string, getting the original index names, etc. I added them to the prototype but they are also non-enumerable, for...in loops still work. I didn't take time to test it on non-primitives, but it works fine for strings, numbers, etc.

    0 讨论(0)
提交回复
热议问题