Lặp lại một mảng liên kết Javascript theo thứ tự đã sắp xếp


109

Giả sử tôi có một mảng kết hợp Javascript (còn gọi là băm, hay còn gọi là từ điển):

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

Làm cách nào để tôi có thể lặp lại các phím theo thứ tự đã sắp xếp? Nếu nó giúp đơn giản hóa mọi thứ, tôi thậm chí không cần các giá trị (tất cả chúng chỉ là số 1).


11
tại sao bạn sử dụng cấu trúc Array () mới và sau đó sử dụng nó như một đối tượng?
Luke Schafer

@Luke .. Tôi cũng làm điều này lúc đầu, đến từ nền tảng PHP. Bây giờ tôi đã học được mặc dù :)
alex

20
@Luke: 'Có vẻ như tôi còn thiếu kinh nghiệm. Bạn có thể đăng cách chính xác trong một câu trả lời?
mike

4
Bạn có thể chỉ cần tạo bất kỳ đối tượng nào. Trong Javascript không có sự khác biệt giữa từ điển / "mảng được đặt tên" và đối tượng thông thường. Do đó, bạn có thể truy cập a ['b'] với ab và ngược lại. Cách ngắn nhất để tạo một đối tượng là a = {};.
Lodewijk

Câu trả lời:


124

Bạn không thể lặp lại chúng trực tiếp, nhưng bạn có thể tìm thấy tất cả các khóa và sau đó chỉ cần sắp xếp chúng.

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;    

function keys(obj)
{
    var keys = [];

    for(var key in obj)
    {
        if(obj.hasOwnProperty(key))
        {
            keys.push(key);
        }
    }

    return keys;
}

keys(a).sort(); // ["a", "b", "z"]

Tuy nhiên, không cần đặt biến 'a' thành một mảng. Bạn thực sự chỉ đang sử dụng nó như một đối tượng và nên tạo nó như thế này:

var a = {};
a["key"] = "value";

28
Bạn nên luôn kiểm tra trong forvòng lặp nếu obj.hasOwnProperty(key).
viam0Zah

3
@Lalit - nếu bạn đang đề cập đến nhận xét của Torok, đó là bởi vì bạn không có bất kỳ thứ gì can thiệp vào nguyên mẫu của đối tượng, điều mà bạn không thể dựa vào.
Luke Schafer

1
+1 tới Torok. Sẽ rất tuyệt nếu câu trả lời bao gồm hasOwnProperty ().
Jon Onstott

136

Bạn có thể sử dụng phương thức tích hợp Object.keys :

var sorted_keys = Object.keys(a).sort()

(Lưu ý: điều này không hoạt động trong các trình duyệt rất cũ không hỗ trợ EcmaScript5, đặc biệt là IE6, 7 và 8. Để biết thống kê chi tiết cập nhật, hãy xem bảng này )


@ michael667 Có lẽ vì IE 7 và 8 vẫn được sử dụng rộng rãi (tiếc là, cảm ơn bạn MS)
Alexander Reifinger

1
IE7 ở mức 0,5% và IE8 ở mức 8% tính đến thời điểm hiện tại, thật may mắn.
molnarg

3
if (!Object.keys) { Object.keys = function (obj) { var op, result = []; for (op in obj) { if (obj.hasOwnProperty(op) { result.push(op) } } return result }
Jordan Reiter

Tôi thích cái này, cảm ơn bạn. đây là mã của tôi tận dụng điều này, $ (Object.keys (list)). map (function (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat cho chuỗi truy vấn url
Elaine

1
Cập nhật năm 2016: Đây có lẽ nên là câu trả lời được chấp nhận
rinogo

14

bạn thậm chí có thể tạo nguyên mẫu nó trên đối tượng:

Object.prototype.iterateSorted = function(worker)
{
    var keys = [];
    for (var key in this)
    {
        if (this.hasOwnProperty(key))
            keys.push(key);
    }
    keys.sort();

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

và cách sử dụng:

var myObj = { a:1, b:2 };
myObj.iterateSorted(function(value)
{
    alert(value);
} 

3
Tôi đã ủng hộ câu trả lời này, nó có vẻ khá tốt, nhưng hóa ra nó phá vỡ jquery :( stackoverflow.com/questions/1827458/… và nói chung được coi là một ý kiến ​​rất tồi "Bạn không bao giờ nên mở rộng Object.prototype. Nó còn làm được nhiều hơn thế break jQuery; nó hoàn toàn phá vỡ tính năng "object-as-hashtables" của Javascript. Đừng làm vậy. Bạn có thể hỏi John Resig và anh ấy sẽ nói với bạn điều tương tự. "
msanjay 14/12/12

Bạn biết gì? Tôi ghét nguyên mẫu quá :) Tôi không bao giờ sử dụng chúng và tích cực không khuyến khích việc sử dụng chúng. Tôi đã cảm thấy như vậy cách đây 3,5 năm khi tôi viết câu trả lời này, nhưng dù sao cũng đề xuất nó ... cảm ơn bạn đã cung cấp thông tin. Là một sang một bên, nó KHÔNG NÊN phá vỡ các khuôn khổ như họ luôn luôn nên sử dụng hasOwnProperty khi iterating đối tượng
Luke Schafer

Đây là một ví dụ sử dụng các giá trị thay vì các khóa để sắp xếp trong khi vẫn duy trì key -> valuemối quan hệ.
Xeoncross

6

Tôi đồng ý với câu trả lời của Swingley và tôi nghĩ rằng đó là một điểm quan trọng mà rất nhiều giải pháp phức tạp hơn này còn thiếu. Nếu bạn chỉ quan tâm đến các khóa trong mảng kết hợp và tất cả các giá trị là '1', thì chỉ cần lưu trữ các 'khóa' dưới dạng giá trị trong một mảng.

Thay vì:

var a = { b:1, z:1, a:1 };
// relatively elaborate code to retrieve the keys and sort them

Sử dụng:

var a = [ 'b', 'z', 'a' ];
alert(a.sort());

Một nhược điểm của điều này là bạn không thể xác định xem một khóa cụ thể có được đặt dễ dàng hay không. Xem câu trả lời này cho hàm javascript inArray để biết câu trả lời cho vấn đề đó. Một vấn đề với giải pháp được trình bày a.hasValue('key')là sẽ hơi chậm hơn a['key']. Điều đó có thể có hoặc có thể không quan trọng trong mã của bạn.


3

Không có cách ngắn gọn nào để thao tác trực tiếp các "phím" của một đối tượng Javascript. Nó không thực sự được thiết kế cho điều đó. Bạn có quyền tự do đặt dữ liệu của mình vào thứ gì đó tốt hơn đối tượng thông thường (hoặc Mảng, như mã mẫu của bạn gợi ý) không?

Nếu vậy, và nếu câu hỏi của bạn có thể được diễn đạt lại là "Tôi nên sử dụng đối tượng giống như từ điển nào nếu tôi muốn lặp lại các khóa theo thứ tự được sắp xếp?" thì bạn có thể phát triển một đối tượng như thế này:

var a = {
  keys : new Array(),
  hash : new Object(),
  set : function(key, value) {
    if (typeof(this.hash[key]) == "undefined") { this.keys.push(key); }
    this.hash[key] = value;
  },
  get : function(key) {
    return this.hash[key];
  },
  getSortedKeys : function() {
    this.keys.sort();
    return this.keys;
  }
};

// sample use
a.set('b',1);
a.set('z',1);
a.set('a',1);
var sortedKeys = a.getSortedKeys();
for (var i in sortedKeys) { print(sortedKeys[i]); }

Nếu bạn không có quyền kiểm soát thực tế là dữ liệu nằm trong một đối tượng thông thường, tiện ích này sẽ chuyển đổi đối tượng thông thường thành từ điển đầy đủ chức năng của bạn:

a.importObject = function(object) {
  for (var i in object) { this.set(i, object); }
};

Đây là một định nghĩa đối tượng (thay vì một hàm tạo có thể tái sử dụng) để đơn giản hóa; chỉnh sửa theo ý muốn.


2

Lấy các khóa trong forvòng lặp đầu tiên , sắp xếp nó, sử dụng kết quả đã sắp xếp trong forvòng lặp thứ hai .

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

var b = [];
for (k in a) b.push(k);
b.sort();
for (var i = 0; i < b.length; ++i) alert(b[i]);

2

Bạn có thể sử dụng keyshàm từ thư viện underscore.js để lấy các khóa, sau đó sort()dùng phương thức mảng để sắp xếp chúng:

var sortedKeys = _.keys(dict).sort();

Các keyschức năng trong mã nguồn của gạch:

// Retrieve the names of an object's properties.
// Delegates to **ECMAScript 5**'s native `Object.keys`
_.keys = nativeKeys || function(obj) {
    if (obj !== Object(obj)) throw new TypeError('Invalid object');
    var keys = [];
    for (var key in obj) if (_.has(obj, key)) keys.push(key);
    return keys;
};    

// Shortcut function for checking if an object has a given property directly
// on itself (in other words, not on a prototype).
_.has = function(obj, key) {
    return hasOwnProperty.call(obj, key);
};

0
<script type="text/javascript">
    var a = {
        b:1,
        z:1,
        a:1
    }; // your JS Object
    var keys = [];
    for (key in a) {
        keys.push(key);
    }
    keys.sort();
    var i = 0;
    var keyslen = keys.length;
    var str = '';
    //SORTED KEY ITERATION
    while (i < keyslen) {
        str += keys[i] + '=>' + a[keys[i]] + '\n';
        ++i;
    }
    alert(str);
    /*RESULT:
    a=>1
    b=>1
    z=>1
    */
</script>

0

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;


var keys=Object.keys(a).sort();
for(var i=0,key=keys[0];i<keys.length;key=keys[++i]){
  document.write(key+' : '+a[key]+'<br>');
}


0

Tôi thực sự thích ý tưởng nguyên mẫu của @ luke-schafer, nhưng cũng nghe những gì anh ấy đang nói về các vấn đề với nguyên mẫu. Điều gì về sử dụng một chức năng đơn giản?

function sortKeysAndDo( obj, worker ) {
  var keys = Object.keys(obj);
  keys.sort();
  for (var i = 0; i < keys.length; i++) {
     worker(keys[i], obj[keys[i]]);
  }
}

function show( key, value ) {
  document.write( key + ' : ' + value +'<br>' );
}

var a = new Array();
a['b'] = 1;
a['z'] = 1;
a['a'] = 1;

sortKeysAndDo( a, show);

var my_object = { 'c': 3, 'a': 1, 'b': 2 };

sortKeysAndDo( my_object, show);

Điều này dường như loại bỏ các vấn đề với nguyên mẫu và vẫn cung cấp một trình lặp được sắp xếp cho các đối tượng. Tuy nhiên, tôi không thực sự là một chuyên gia về JavaScript, vì vậy tôi rất muốn biết liệu giải pháp này có những lỗ hổng ẩn mà tôi đã bỏ qua hay không.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.