2010-05-09 6 views
20

JavaScript 개체에는 특성에 따라 순서가 저장되지 않습니다 (사양에 따라 다름). Firefox는 for...in 루프를 사용할 때 속성의 정의 순서를 유지하는 것으로 보입니다. 이 행동이 내가 의지 할 수있는 것입니까? 순서가 지정된 해시 유형을 구현하는 JavaScript 코드가 어딘가에 있습니까?JavaScript의 순서가 지정된 해시

+0

페이지에 http : //stackoverflow.com/questions/280713/elements-order-for-in-loop-in-javascript –

답변

8

이 질문은 최고의 검색 결과로 나타납니다. 순서가 지정된 해시를 찾지 못한 후에이 작은 코프 스크립트를 작성했습니다. 다행히도이 페이지에 방문하는 사람들을 도울 것입니다.

## 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 
+7

OP에서 JavaScript로 솔루션을 요청했습니다.> 내 포트는 아래를 참조하십시오. –

+0

@FloLedermann 감사합니다. http://js2.coffee/도 이러한 상황에서 일반적으로 도움이 될 수 있습니다. – crizCraig

+0

'@m_vals [k]'가 아니라면'm_keys' 배열에 키를 푸시하는 것처럼 보입니다. 아마도 if (! (k in this.m_vals))'(CoffeeScript를 모르기 때문에 그렇게 쓰는)해야 할 것입니다. –

26

아니요, Object type is specified to be an unordered collection of properties부터 사용할 수 없습니다. (또는 : 개체가 정렬되지 않은 속성 모음 인 경우에만 의존 할 수 있습니다.)

주문한 해시 세트를 원할 경우 직접 구현해야합니다.

+0

운전하지 마십시오 downvotes 빨아? – Pointy

+3

질문의 첫 부분 만 응답하고 해결책을 시도하지 않기 때문에 Downvoting –

4

트릭 중 하나는 규칙적인 순서가 지정되지 않은 해시에 데이터를 저장 한 다음 원하는 순서를 배열에 저장하는 것입니다. JS에서는 순서 배열을 해시 자체의 일부로 만들 수도 있습니다.

var myHash = { 
    a: "2", 
    b: "3", 
    c: "1" 
}; 

myHash.order = [ myHash.c, myHash.a, myHash.b ]; 

alert("I can access values by key. Here's B: " + myHash.b); 
var message = "I can access also loop over the values in order: "; 

for (var i=0;i<myHash.order.length;i++) 
{ 
    message = message + myHash.order[i] + ", "; 
} 

alert(message) 

세련된 것은 아니지만 작업이 완료됩니다.

+0

이 솔루션은 주문에 사용 된 메커니즘이 캡슐화되지 않았고 클라이언트 코드가 내부적으로 작동하도록 알기 때문에 매우 취약합니다. ... 일회용 프로젝트는 괜찮지 만 생산 준비가 된 IMO는 아닐 수도 있습니다. –

0

실현이 늦었지만 다른 곳에서는 찾을 수 없었습니다. * UPDATE 열거 할 수없는 필수 메서드 및 속성을 추가했습니다. 빠른 ES 5 구현 (필요에 따라 polyfill) :

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 
     } 
    } 
}) 

여기 어떻게됩니까 대신 할당의 Object.defineProperty()를 사용하여 할 수있는 우리가 for...in 또는 Object.keys()를 사용하여 해시를 반복 이렇게하면 속성이 아닌 열거 할 것입니다 우리 순서가 지정된 값만 가져 오지만 hash.propertyname을 확인하면됩니다. 다른 객체 (hash())의 삽입, 제거, 동화, 키순 정렬, 값순 정렬, 배열 또는 문자열 변환, 원본 색인 이름 가져 오기 등의 메소드가 있습니다. 프로토 타입에 추가했지만 그 외에도 있습니다 -enumerable, for...in 루프는 여전히 작동합니다. 비 프리미티브에서 테스트하는 데 시간이 걸리지 않았지만 문자열, 숫자 등에서 정상적으로 작동합니다.

18

@Vardhan의 대답은 일반 JavaScript로, 클래식 OO 대신 클로저를 사용하고 삽입) 방법 :

function makeOrderedHash() { 
    var keys = []; 
    var vals = {}; 
    return { 
     push: function(k,v) { 
      if (!vals[k]) keys.push(k); 
      vals[k] = v; 
     }, 
     insert: function(pos,k,v) { 
      if (!vals[k]) { 
       keys.splice(pos,0,k); 
       vals[k] = v; 
      } 
     }, 
     val: function(k) {return vals[k]}, 
     length: function(){return keys.length}, 
     keys: function(){return keys}, 
     values: function(){return vals} 
    }; 
}; 

var myHash = makeOrderedHash(); 
0

매우 간단한 방법은 배열을 사용하여 주문을 저장하는 것입니다. 사용자 정의 비교 기능을 작성하여 필요한 순서를 설정해야합니다. 아래쪽은 배열을 정렬하고 관계를 추적 할 때마다 매번 해시 테이블을 변경해야합니다. 변경 해시 목록을 유지 관리해야하는 것은 그리 깔끔하지 않습니다. 당신이하는 특성이 삽입 된 주문 알에만 관심이 있다면

var order=[]; 
var hash={"h1":4,"h2":2,"h3":3,"h4":1}; 

function cmp(a,b) { 
    if (hash[a] < hash[b]) return -1; 
    if (hash[a] > hash[b]) return 1; 
    return 0; 
} 

// Add initial hash object to order array 
for(i in hash) order.push(i); 
order.sort(cmp); 
// h4:1 h2:2 h3:3 h1:4 

// Add entry 
hash['h5']=2.5; 
order.push('h5'); 
order.sort(cmp); 
// h4:1 h2:2 h5:2.5 h3:3 h1:4 

// Delete entry 
order.splice(order.indexOf('h5'), 1); 
delete hash['h5']; 
// h4:1 h2:2 h3:3 h1:4 

// Display ordered hash array (with keys) 
for(i in order) console.log(order[i],hash[order[i]]); 
0

, @Craig_Walker 솔루션을 촬영, 쉬운 솔루션은 다음과 같습니다 특히

var obj ={ } 
var order = []; 

function add(key, value) { 
    obj[key] = value; 
    order.push(key); 
} 

function getOldestKey() { 
    var key = order.shift(); 
    return obj[key] 
} 

function getNewsetKey() { 
    var key = order.pop(); 
    return obj[key] 
} 
13

자바 스크립트 2016, ECMA 스크립트 (6), 지원 Map built-in class.

Map 객체는 요소를 삽입 순서대로 반복합니다. for ... of 루프는 각 반복마다 [key, value] 배열을 반환합니다.

그게 당신이 필요합니다. (즉하지만,이 데이터 구조에 대한 설명에서 처음으로 정보를 왜 궁금하다.) 예를 들어

,

m = new Map() 

m.set(3,'three') 
m.set(1,'one') 
m.set(2,'two') 

m // Map { 3 => 'three', 1 => 'one', 2 => 'two' } 

[...m.keys()] // [ 3, 1, 2 ] 

또는 docs에서 예 :

var myMap = new Map(); 
myMap.set(0, 'zero'); 
myMap.set(1, 'one'); 

myMap // Map { 0 => 'zero', 1 => 'one' } 

for (var [key, value] of myMap) { 
    console.log(key + " = " + value); 
} 

for (var key of myMap.keys()) { 
    console.log(key); 
} 

for (var value of myMap.values()) { 
    console.log(value); 
} 

for (var [key, value] of myMap.entries()) { 
    console.log(key + " = " + value); 
} 
관련 문제