วนซ้ำบนอาร์เรย์ Javascript Associative ตามลำดับที่เรียง


109

สมมติว่าฉันมีอาร์เรย์ Javascript Associative (aka hash, aka dictionary):

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

ฉันจะย้ำคีย์ตามลำดับได้อย่างไร ถ้ามันช่วยให้สิ่งต่าง ๆ ง่ายขึ้นฉันไม่ต้องการค่าด้วยซ้ำ (ทั้งหมดเป็นเพียงตัวเลข 1)


11
ทำไมคุณถึงใช้โครงสร้าง Array () ใหม่แล้วใช้มันเหมือนวัตถุ?
Luke Schafer

@ ลุค .. ตอนแรกก็ทำแบบนี้เหมือนกันมาจากพื้นหลัง PHP ฉันได้เรียนรู้แล้ว :)
อเล็กซ์

20
@ ลุค: 'เพราะฉันไม่มีประสบการณ์ดูเหมือนว่า คุณสามารถโพสต์คำตอบที่ถูกต้องได้หรือไม่?
ไมค์

4
คุณสามารถสร้างวัตถุใด ๆ ใน Javascript ไม่มีความแตกต่างระหว่างพจนานุกรม / "ชื่ออาร์เรย์" กับวัตถุทั่วไป คุณจึงสามารถเข้าถึง ['b'] ด้วย ab และในทางกลับกัน a = {};ทางที่สั้นที่สุดในการสร้างวัตถุ
Lodewijk

คำตอบ:


124

คุณไม่สามารถทำซ้ำได้โดยตรง แต่คุณสามารถค้นหาคีย์ทั้งหมดแล้วเรียงลำดับได้

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"]

อย่างไรก็ตามไม่จำเป็นต้องทำให้ตัวแปร 'a' เป็นอาร์เรย์ คุณแค่ใช้มันเป็นวัตถุจริงๆและควรสร้างแบบนี้:

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

28
คุณควรตรวจสอบforลูปobj.hasOwnProperty(key)เสมอถ้า
viam0Zah

3
@Lalit - หากคุณอ้างถึงความคิดเห็นของ Torok นั่นเป็นเพราะคุณไม่มีอะไรรบกวนต้นแบบของวัตถุซึ่งคุณไม่สามารถพึ่งพาได้
Luke Schafer

1
+1 ให้กับ Torok คงจะดีไม่น้อยหากคำตอบมี hasOwnProperty ()
Jon Onstott

136

คุณสามารถใช้Object.keysในตัวเมธอด:

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

(หมายเหตุ: สิ่งนี้ใช้ไม่ได้กับเบราว์เซอร์รุ่นเก่าที่ไม่รองรับ EcmaScript5 โดยเฉพาะอย่างยิ่ง IE6, 7 และ 8 สำหรับสถิติล่าสุดโดยละเอียดโปรดดูตารางนี้ )


@ michael667 อาจเป็นเพราะ IE 7 และ 8 ยังคงใช้กันอย่างแพร่หลาย (โชคไม่ดีขอบคุณ MS)
Alexander Reifinger

1
IE7 อยู่ที่ 0.5% และ IE8 อยู่ที่ 8% ณ ตอนนี้โชคดี
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

ฉันรักคนนี้ขอบคุณ นี่คือรหัสของฉันที่ใช้ประโยชน์จากสิ่งนี้ $ (Object.keys (รายการ)) แผนที่ (ฟังก์ชัน (i, e) {return n + '=' + list [n];}). get (). join ('&'); // concat for url querystring
Elaine

1
การอัปเดตปี 2016: นี่น่าจะเป็นคำตอบที่ยอมรับได้
rinogo

14

คุณสามารถสร้างต้นแบบลงบนวัตถุ:

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

และการใช้งาน:

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

3
ฉันเพิ่มคะแนนคำตอบนี้ดูเหมือนจะดี แต่กลับกลายเป็นว่ามันทำให้ jquery แตก :( stackoverflow.com/questions/1827458/…และโดยทั่วไปถือว่าเป็นความคิดที่แย่มาก "คุณไม่ควรขยาย Object.prototype มันทำได้มากกว่านี้ ทำลาย jQuery; มันทำลายคุณสมบัติ "object-
as-แฮชแท็ก

คุณรู้อะไรไหม? ฉันเกลียดต้นแบบด้วย :) ฉันไม่เคยใช้มันและไม่สนับสนุนการใช้งานของมัน ฉันรู้สึกแบบนี้เมื่อ 3.5 ปีที่แล้วเมื่อฉันเขียนคำตอบนี้ แต่ก็แนะนำมันอยู่ดี ... ขอบคุณที่ให้ข้อมูล นอกเหนือจากนั้นก็ไม่ควรทำลายกรอบงานเนื่องจากควรใช้ hasOwnProperty เสมอเมื่อทำซ้ำวัตถุ
Luke Schafer

นี่คือตัวอย่างการใช้ค่าแทนคีย์สำหรับการเรียงลำดับในขณะที่ยังคงรักษาkey -> valueความสัมพันธ์ไว้
Xeoncross

6

ฉันเห็นด้วยกับคำตอบของ Swingleyและฉันคิดว่ามันเป็นจุดสำคัญที่ไม่มีโซลูชันที่ซับซ้อนมากขึ้นเหล่านี้ หากคุณสนใจเฉพาะคีย์ในอาร์เรย์ที่เชื่อมโยงและค่าทั้งหมดเป็น '1' ให้เก็บ 'คีย์' เป็นค่าในอาร์เรย์

แทน:

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

ใช้:

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

ข้อเสียเปรียบประการหนึ่งคือคุณไม่สามารถระบุได้ว่าจะตั้งค่าคีย์เฉพาะอย่างง่ายดายหรือไม่ ดูคำตอบสำหรับฟังก์ชัน javascript inArrayสำหรับคำตอบสำหรับปัญหานั้น ปัญหาหนึ่งที่มีเป็นวิธีการแก้ที่นำเสนอว่าเป็นไปได้ช้ากว่าเล็กน้อยa.hasValue('key') a['key']ซึ่งอาจมีหรือไม่สำคัญในโค้ดของคุณ


3

ไม่มีวิธีที่กระชับในการจัดการ "คีย์" ของวัตถุ Javascript โดยตรง มันไม่ได้ออกแบบมาเพื่อสิ่งนั้นจริงๆ คุณมีอิสระในการใส่ข้อมูลของคุณในสิ่งที่ดีกว่าวัตถุทั่วไป (หรือ Array ตามที่โค้ดตัวอย่างของคุณแนะนำ) หรือไม่?

ถ้าเป็นเช่นนั้นและหากคำถามของคุณสามารถเปลี่ยนวลีได้ว่า "ฉันควรใช้อ็อบเจกต์ที่มีลักษณะเหมือนพจนานุกรมใดหากต้องการย้ำคีย์ตามลำดับที่เรียง" จากนั้นคุณอาจพัฒนาวัตถุเช่นนี้:

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

หากคุณไม่สามารถควบคุมข้อเท็จจริงที่ว่าข้อมูลอยู่ในออบเจ็กต์ปกติยูทิลิตี้นี้จะแปลงอ็อบเจ็กต์ปกติเป็นพจนานุกรมที่ใช้งานได้อย่างสมบูรณ์:

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

นี่คือนิยามวัตถุ (แทนฟังก์ชันตัวสร้างที่ใช้ซ้ำได้) เพื่อความเรียบง่าย แก้ไขได้ตามต้องการ


2

รับคีย์ในforลูปแรกเรียงลำดับใช้ผลการเรียงลำดับในforลูปที่2

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

คุณสามารถใช้keysฟังก์ชันจากไลบรารีunderscore.jsเพื่อรับคีย์จากนั้นใช้sort()วิธีอาร์เรย์เพื่อจัดเรียง:

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

keysฟังก์ชั่นในรหัสที่มาขีดที่:

// 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

ฉันชอบไอเดียต้นแบบของ @luke-schafer มาก แต่ก็ได้ยินสิ่งที่เขาพูดเกี่ยวกับปัญหาของต้นแบบด้วย แล้วการใช้ฟังก์ชันง่ายๆล่ะ?

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

ดูเหมือนว่าจะขจัดปัญหาเกี่ยวกับต้นแบบและยังคงมีตัววนซ้ำแบบเรียงลำดับสำหรับวัตถุ ฉันไม่ใช่กูรู JavaScript จริงๆดังนั้นฉันอยากทราบว่าโซลูชันนี้มีข้อบกพร่องซ่อนอยู่หรือไม่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.