วิธีการจัดเรียงอาร์เรย์ที่เชื่อมโยงตามค่าใน Javascript


84

ฉันมีอาร์เรย์ที่เชื่อมโยง:

array["sub2"] = 1;
array["sub0"] = -1;
array["sub1"] = 0;
array["sub3"] = 1;
array["sub4"] = 0;

วิธีใดเป็นวิธีที่สง่างามที่สุดในการเรียงลำดับ (จากมากไปหาน้อย) ตามค่าโดยที่ผลลัพธ์จะเป็นอาร์เรย์ที่มีดัชนีตามลำดับนี้:

sub2, sub3, sub1, sub4, sub0

1
เนื่องจากคุณสมบัติของอ็อบเจ็กต์ไม่มีลำดับที่กำหนดเป็นภาษาคุณจึงทำไม่ได้ (ยกเว้นในบางเอนจิน JS โดยขึ้นอยู่กับวิธีเฉพาะที่ใช้คุณสมบัติ)
Quentin

คำตอบ:


115

Javascript ไม่มี "Associative arrays" อย่างที่คุณคิด แต่คุณสามารถตั้งค่าคุณสมบัติของอ็อบเจ็กต์โดยใช้ไวยากรณ์แบบอาร์เรย์ได้ (ดังตัวอย่างของคุณ) รวมถึงความสามารถในการทำซ้ำคุณสมบัติของอ็อบเจ็กต์

ผลสรุปของสิ่งนี้คือไม่มีการรับประกันเกี่ยวกับลำดับที่คุณวนซ้ำคุณสมบัติดังนั้นจึงไม่มีอะไรเหมือนกับการจัดเรียงสำหรับพวกเขา แต่คุณจะต้องแปลงคุณสมบัติวัตถุของคุณเป็นอาร์เรย์ "จริง" (ซึ่งรับประกันลำดับ) นี่คือข้อมูลโค้ดสำหรับการแปลงออบเจ็กต์เป็นอาร์เรย์ของสองทูเปิล (อาร์เรย์สององค์ประกอบ) โดยจัดเรียงตามที่คุณอธิบายจากนั้นจึงวนซ้ำทับ:

var tuples = [];

for (var key in obj) tuples.push([key, obj[key]]);

tuples.sort(function(a, b) {
    a = a[1];
    b = b[1];

    return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < tuples.length; i++) {
    var key = tuples[i][0];
    var value = tuples[i][1];

    // do something with key and value
}

คุณอาจพบว่ามันเป็นธรรมชาติมากกว่าที่จะรวมสิ่งนี้ไว้ในฟังก์ชันที่เรียกกลับ:

function bySortedValue(obj, callback, context) {
  var tuples = [];

  for (var key in obj) tuples.push([key, obj[key]]);

  tuples.sort(function(a, b) {
    return a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : 0
  });

  var length = tuples.length;
  while (length--) callback.call(context, tuples[length][0], tuples[length][1]);
}

bySortedValue({
  foo: 1,
  bar: 7,
  baz: 3
}, function(key, value) {
  document.getElementById('res').innerHTML += `${key}: ${value}<br>`
});
<p id='res'>Result:<br/><br/><p>


1
ฟังก์ชัน tuples.sort สามารถล้างได้ถึง tuples.sort (ฟังก์ชัน (a, b) {return a [1] - b [1];});
stot

2
@stot - หากค่าของคุณเป็นตัวเลขทั้งหมด (ตามตัวอย่างของผู้ถาม) อย่างแน่นอน ดูเหมือนว่าฉันจะจัดเตรียมฟังก์ชันการเปรียบเทียบที่ใช้งานได้กับสตริงด้วย :-)
Ben Blank

@ เบ็น: ใช่คุณพูดถูกรุ่นที่ให้ไว้นั้นกว้างกว่า ;-)
ต็อต

@ เบ็นขอบคุณมากสำหรับโพสต์นี้ เกี่ยวกับฟังก์ชันการเปรียบเทียบสตริงจะใช้การเปรียบเทียบค่า ASCII หรือไม่
jjwdesign

@jjwdesign สมมติว่าสตริงไม่ได้เข้ารหัสมันจะจัดเรียงตามจุดรหัส Unicode
Ben Blank

86

แทนที่จะแก้ไขความหมายของ 'Associative array' ฉันคิดว่านี่คือสิ่งที่คุณต้องการ:

function getSortedKeys(obj) {
    var keys = keys = Object.keys(obj);
    return keys.sort(function(a,b){return obj[b]-obj[a]});
}

สำหรับเบราว์เซอร์รุ่นเก่าให้ใช้สิ่งนี้แทน:

function getSortedKeys(obj) {
    var keys = []; for(var key in obj) keys.push(key);
    return keys.sort(function(a,b){return obj[b]-obj[a]});
}

คุณถ่ายโอนข้อมูลในออบเจ็กต์ (เช่นของคุณ) และรับอาร์เรย์ของคีย์ - eh properties - back เรียงลำดับจากมากไปหาน้อยตามค่า (ตัวเลข) ของ eh ค่าของ eh วัตถุ

สิ่งนี้ใช้ได้เฉพาะเมื่อค่าของคุณเป็นตัวเลข ทวีคูณเล็กน้อยfunction(a,b)ในนั้นเพื่อเปลี่ยนกลไกการเรียงลำดับให้ทำงานจากน้อยไปมากหรือทำงานเพื่อหาstringค่า (ตัวอย่าง) เหลือไว้เป็นแบบฝึกหัดสำหรับผู้อ่าน.


1
สิ่งนี้ทำงานได้อย่างสมบูรณ์และเป็นรหัสที่น้อยกว่าคำตอบข้างต้น
jshill103

1
ฉันควรทราบว่าเบราว์เซอร์ส่วนใหญ่ในปัจจุบันสนับสนุน Object.keys () - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
commonpike

ทำไม object.keys ถึงดีกว่า?
john ktejik

1
@johnktejik คำตอบนี้มาจากปี 2012 :-) Object.keys () เป็นมาตรฐานในปัจจุบันและสั้นกว่าและอาจเร็วกว่า
commonpike

2
... :) ฉันเหนื่อยให้ฉันลบสิ่งนี้
manu

16

การอภิปรายอย่างต่อเนื่องและการแก้ปัญหาอื่น ๆ ที่ครอบคลุมในวิธีการจัดเรียงอาร์เรย์ (เชื่อมโยง) ตามค่า? ด้วยทางออกที่ดีที่สุด (สำหรับกรณีของฉัน) โดยsaml (อ้างถึงด้านล่าง)

อาร์เรย์สามารถมีดัชนีตัวเลขเท่านั้น คุณต้องเขียนสิ่งนี้ใหม่เป็น Object หรือ Array of Objects

var status = new Array();
status.push({name: 'BOB', val: 10});
status.push({name: 'TOM', val: 3});
status.push({name: 'ROB', val: 22});
status.push({name: 'JON', val: 7});

หากคุณชอบstatus.pushวิธีนี้คุณสามารถจัดเรียงได้ด้วย:

status.sort(function(a,b) {
    return a.val - b.val;
});

ยอดเยี่ยม! สิ่งที่ต้องทำและเรียงลำดับอัลฟาในชื่อ: ส่งคืน a.name> b.name
mpemburn

1
สำหรับการเรียงลำดับตามตัวอักษรให้เปรียบเทียบค่าสตริงในกรณีเดียวกันเนื่องจากsort()ถือว่าต่างกัน มันทำให้ฉันวุ่นวายไปหนึ่งชั่วโมงจนกระทั่งฉันเจอstackoverflow.com/questions/6712034/… ตัวอย่างนี้ a.name.toLowerCase() > b.name.toLowerCase()
Dylan Valade

5

ไม่มีสิ่งที่เรียกว่า "Associative array" ใน JavaScript สิ่งที่คุณมีมีเพียงวัตถุเก่า ๆ ธรรมดา ๆ แน่นอนว่าพวกเขาทำงานคล้ายกับอาร์เรย์ที่เชื่อมโยงกันและมีคีย์ที่พร้อมใช้งาน แต่ไม่มีความหมายเกี่ยวกับลำดับของคีย์

คุณสามารถเปลี่ยนวัตถุของคุณให้เป็นอาร์เรย์ของวัตถุ (คู่คีย์ / ค่า) และจัดเรียงว่า:

function sortObj(object, sortFunc) {
  var rv = [];
  for (var k in object) {
    if (object.hasOwnProperty(k)) rv.push({key: k, value:  object[k]});
  }
  rv.sort(function(o1, o2) {
    return sortFunc(o1.key, o2.key);
  });
  return rv;
}

จากนั้นคุณจะเรียกสิ่งนั้นด้วยฟังก์ชันเปรียบเทียบ


+1 คุณเอาชนะฉันไปได้ ฉันกำลังเขียนคำอธิบายและโค้ดที่คล้ายกันมาก
gonchuki

4

นี่คือรูปแบบของคำตอบของ ben blank หากคุณไม่ชอบสิ่งที่มี

วิธีนี้ช่วยให้คุณประหยัดอักขระได้ไม่กี่ตัว

var keys = [];
for (var key in sortme) {
  keys.push(key);
}

keys.sort(function(k0, k1) {
  var a = sortme[k0];
  var b = sortme[k1];
  return a < b ? -1 : (a > b ? 1 : 0);
});

for (var i = 0; i < keys.length; ++i) {
  var key = keys[i];
  var value = sortme[key];
  // Do something with key and value.
}

4

ไม่ต้องยุ่งยากโดยไม่จำเป็น ...

function sortMapByValue(map)
{
    var tupleArray = [];
    for (var key in map) tupleArray.push([key, map[key]]);
    tupleArray.sort(function (a, b) { return a[1] - b[1] });
    return tupleArray;
}

ผลลัพธ์นี้เป็นอาร์เรย์อาร์เรย์ไม่ใช่แผนที่
Daniel

var stuff = {"a":1 , "b":3 , "c":0 } sortMapByValue(stuff) [Array[2], Array[2], Array[2]]
Daniel

ฉันคิดว่านี่คือสิ่งที่สะอาดที่สุด เพียงเพิ่มการแปลงลงในแผนที่ในตอนท้าย
Daniel

1

ฉันใช้ $. แต่ละ jquery แต่คุณสามารถทำด้วย for loop การปรับปรุงคือ:

        //.ArraySort(array)
        /* Sort an array
         */
        ArraySort = function(array, sortFunc){
              var tmp = [];
              var aSorted=[];
              var oSorted={};

              for (var k in array) {
                if (array.hasOwnProperty(k)) 
                    tmp.push({key: k, value:  array[k]});
              }

              tmp.sort(function(o1, o2) {
                    return sortFunc(o1.value, o2.value);
              });                     

              if(Object.prototype.toString.call(array) === '[object Array]'){
                  $.each(tmp, function(index, value){
                      aSorted.push(value.value);
                  });
                  return aSorted;                     
              }

              if(Object.prototype.toString.call(array) === '[object Object]'){
                  $.each(tmp, function(index, value){
                      oSorted[value.key]=value.value;
                  });                     
                  return oSorted;
              }               
     };

ตอนนี้คุณสามารถทำได้

    console.log("ArraySort");
    var arr1 = [4,3,6,1,2,8,5,9,9];
    var arr2 = {'a':4, 'b':3, 'c':6, 'd':1, 'e':2, 'f':8, 'g':5, 'h':9};
    var arr3 = {a: 'green', b: 'brown', c: 'blue', d: 'red'};
    var result1 = ArraySort(arr1, function(a,b){return a-b});
    var result2 = ArraySort(arr2, function(a,b){return a-b});
    var result3 = ArraySort(arr3, function(a,b){return a>b});
    console.log(result1);
    console.log(result2);       
    console.log(result3);

1

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

// aao is the "associative array" you need to "sort"
Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

สิ่งนี้สามารถใช้ได้อย่างง่ายดายและใช้งานได้ดีในกรณีเฉพาะที่นี่เพื่อให้คุณสามารถทำ:

let aoo={};
aao["sub2"]=1;
aao["sub0"]=-1;
aao["sub1"]=0;
aao["sub3"]=1;
aao["sub4"]=0;

let sk=Object.keys(aao).sort(function(a,b){return aao[b]-aao[a]});

// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
 // do something with sk[i] or aoo[sk[i]]
}

นอกจากนี้ฉันยังมีฟังก์ชัน "ทั่วไป" เพิ่มเติมที่คุณสามารถใช้เพื่อจัดเรียงได้แม้ในสถานการณ์ที่กว้างขึ้นและผสมผสานการปรับปรุงที่ฉันเพิ่งแนะนำกับแนวทางของคำตอบโดยBen Blank (การเรียงลำดับค่าสตริงด้วย) และPopeJohnPaulII ( การจัดเรียงตามช่องวัตถุ / คุณสมบัติเฉพาะ) และช่วยให้คุณตัดสินใจได้ว่าคุณต้องการลำดับลัคนาหรือลำดับลูกหลานนี่คือ:

// aao := is the "associative array" you need to "sort"
// comp := is the "field" you want to compare or "" if you have no "fields" and simply need to compare values
// intVal := must be false if you need comparing non-integer values
// desc := set to true will sort keys in descendant order (default sort order is ascendant)
function sortedKeys(aao,comp="",intVal=false,desc=false){
  let keys=Object.keys(aao);
  if (comp!="") {
    if (intVal) {
      if (desc) return keys.sort(function(a,b){return aao[b][comp]-aao[a][comp]});
      else return keys.sort(function(a,b){return aao[a][comp]-aao[a][comp]});
    } else {
      if (desc) return keys.sort(function(a,b){return aao[b][comp]<aao[a][comp]?1:aao[b][comp]>aao[a][comp]?-1:0});
      else return keys.sort(function(a,b){return aao[a][comp]<aao[b][comp]?1:aao[a][comp]>aao[b][comp]?-1:0});
    }
  } else {
    if (intVal) {
      if (desc) return keys.sort(function(a,b){return aao[b]-aao[a]});
      else return keys.sort(function(a,b){return aao[a]-aao[b]});
    } else {
      if (desc) return keys.sort(function(a,b){return aao[b]<aao[a]?1:aao[b]>aao[a]?-1:0});
      else return keys.sort(function(a,b){return aao[a]<aao[b]?1:aao[a]>aao[b]?-1:0});
    }
  }
}

คุณสามารถทดสอบฟังก์ชันการทำงานโดยใช้รหัสต่อไปนี้:

let items={};
items['Edward']=21;
items['Sharpe']=37;
items['And']=45;
items['The']=-12;
items['Magnetic']=13;
items['Zeros']=37;
//equivalent to:
//let items={"Edward": 21, "Sharpe": 37, "And": 45, "The": -12, ...};

console.log("1: "+sortedKeys(items));
console.log("2: "+sortedKeys(items,"",false,true));
console.log("3: "+sortedKeys(items,"",true,false));
console.log("4: "+sortedKeys(items,"",true,true));
/* OUTPUT
1: And,Sharpe,Zeros,Edward,Magnetic,The
2: The,Magnetic,Edward,Sharpe,Zeros,And
3: The,Magnetic,Edward,Sharpe,Zeros,And
4: And,Sharpe,Zeros,Edward,Magnetic,The
*/

items={};
items['k1']={name:'Edward',value:21};
items['k2']={name:'Sharpe',value:37};
items['k3']={name:'And',value:45};
items['k4']={name:'The',value:-12};
items['k5']={name:'Magnetic',value:13};
items['k6']={name:'Zeros',value:37};

console.log("1: "+sortedKeys(items,"name"));
console.log("2: "+sortedKeys(items,"name",false,true));
/* OUTPUT
1: k6,k4,k2,k5,k1,k3
2: k3,k1,k5,k2,k4,k6
*/

ดังที่ฉันได้กล่าวไปแล้วคุณสามารถวนซ้ำคีย์ที่เรียงลำดับได้หากคุณต้องการทำสิ่งของต่างๆ

let sk=sortedKeys(aoo);
// now you can loop using the sorted keys in `sk` to do stuffs
for (let i=sk.length-1;i>=0;--i){
 // do something with sk[i] or aoo[sk[i]]
}

สุดท้าย แต่ไม่ท้ายสุดการอ้างอิงที่มีประโยชน์บางอย่างไปยังObject.keysและArray.sort


0

ดังนั้นมันจึงอยู่ที่นั่นและมีคนกำลังมองหาประเภทจากทูเปิล สิ่งนี้จะเปรียบเทียบองค์ประกอบแรกของวัตถุในอาร์เรย์มากกว่าองค์ประกอบที่สองและอื่น ๆ เช่นในตัวอย่างด้านล่างจะเปรียบเทียบก่อนด้วย "a" แล้วตามด้วย "b" เป็นต้น

let arr = [
    {a:1, b:2, c:3},
    {a:3, b:5, c:1},
    {a:2, b:3, c:9},
    {a:2, b:5, c:9},
    {a:2, b:3, c:10}    
]

function getSortedScore(obj) {
    var keys = []; 
    for(var key in obj[0]) keys.push(key);
    return obj.sort(function(a,b){
        for (var i in keys) {
            let k = keys[i];
            if (a[k]-b[k] > 0) return -1;
            else if (a[k]-b[k] < 0) return 1;
            else continue;
        };
    });
}

console.log(getSortedScore(arr))

OUPUTS

 [ { a: 3, b: 5, c: 1 },
  { a: 2, b: 5, c: 9 },
  { a: 2, b: 3, c: 10 },
  { a: 2, b: 3, c: 9 },
  { a: 1, b: 2, c: 3 } ]

-4

คำตอบของ @ commonpike คือ "คนที่ใช่" แต่พอเข้าไปแสดงความคิดเห็น ...

ปัจจุบันเบราว์เซอร์ส่วนใหญ่รองรับ Object.keys()

ใช่ .. Object.keys()เป็นวิธีที่ดีกว่า

แต่จะมีอะไรดีไปกว่านั้น ? มันเข้าแล้วcoffeescript!

sortedKeys = (x) -> Object.keys(x).sort (a,b) -> x[a] - x[b]

sortedKeys
  'a' :  1
  'b' :  3
  'c' :  4
  'd' : -1

[ 'd', 'a', 'b', 'c' ]

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