Làm cách nào để sắp xếp một mảng kết hợp theo các giá trị của nó trong Javascript?


84

Tôi có mảng kết hợp:

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

Cách thanh lịch nhất để sắp xếp (giảm dần) theo các giá trị của nó trong đó kết quả sẽ là một mảng với các chỉ số tương ứng theo thứ tự sau:

sub2, sub3, sub1, sub4, sub0

1
Vì các thuộc tính đối tượng không có thứ tự được xác định theo ngôn ngữ - bạn không thể (ngoại trừ, có lẽ, trong một số công cụ JS tùy thuộc vào cách cụ thể mà chúng đã triển khai thuộc tính).
Quentin

Câu trả lời:


113

Javascript không có "mảng kết hợp" như cách bạn nghĩ về chúng. Thay vào đó, bạn chỉ cần có khả năng thiết lập các thuộc tính của đối tượng bằng cách sử dụng cú pháp giống mảng (như trong ví dụ của bạn), cộng với khả năng lặp qua các thuộc tính của một đối tượng.

Kết quả của điều này là không có gì đảm bảo về thứ tự mà bạn lặp qua các thuộc tính, vì vậy không có gì giống như một sự sắp xếp cho chúng. Thay vào đó, bạn sẽ cần chuyển đổi các thuộc tính đối tượng của mình thành một mảng "true" (đảm bảo thứ tự). Đây là một đoạn mã để chuyển đổi một đối tượng thành một mảng hai bộ (mảng hai phần tử), sắp xếp nó như bạn mô tả, sau đó lặp lại nó:

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
}

Bạn có thể thấy tự nhiên hơn khi bao bọc điều này trong một hàm thực hiện một cuộc gọi lại:

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
có thể dọn sạch hàm tuples.sort thành tuples.sort (function (a, b) {return a [1] - b [1];});
stot

2
@stot - Nếu các giá trị của bạn đều là số (như trong ví dụ của người hỏi), hoàn toàn. Tôi dường như đã lơ đãng cung cấp một hàm so sánh cũng hoạt động với các chuỗi. :-)
Ben Blank

@Ben: vâng, bạn nói đúng, phiên bản được cung cấp tổng quát hơn ;-)
stot 9/12/12

@Ben, cảm ơn bạn rất nhiều về bài đăng này. Liên quan đến chức năng so sánh chuỗi, nó có sử dụng so sánh giá trị ASCII không?
jjwdesign

@jjwdesign, giả sử chuỗi chưa được mã hóa, nó sẽ sắp xếp theo điểm mã Unicode.
Ben Blank

85

Thay vì sửa bạn về ngữ nghĩa của 'mảng kết hợp', tôi nghĩ đây là những gì bạn muốn:

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

đối với các trình duyệt thực sự cũ , hãy sử dụng cái này để thay thế:

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

Bạn kết xuất một đối tượng (giống như của bạn) và nhận được một mảng các khóa - thuộc tính eh - back, được sắp xếp giảm dần theo giá trị (số) của giá trị, eh, của đối tượng.

Điều này chỉ hoạt động nếu các giá trị của bạn là số. Hãy tweet một chút function(a,b)ở đó để thay đổi cơ chế sắp xếp để hoạt động tăng dần hoặc hoạt động cho stringcác giá trị (ví dụ). Còn lại như một bài tập cho người đọc.


1
Đây hoạt động hoàn hảo và là mã ít hơn thì câu trả lời ở trên
jshill103

1
Tôi nên lưu ý rằng hầu hết các trình duyệt hiện nay chỉ hỗ trợ Object.keys () - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
commonpike

tại sao object.keys lại tốt hơn?
john ktejik

1
@johnktejik câu trả lời này là từ năm 2012 :-) Object.keys () là một tiêu chuẩn ngày nay, ngắn hơn và có lẽ nhanh hơn.
commonpike

2
... :) Tôi đã mệt mỏi, hãy để tôi xóa cái này
manu

16

Tiếp tục thảo luận & các giải pháp khác được đề cập tại Cách sắp xếp mảng (liên kết) theo giá trị? với giải pháp tốt nhất (đối với trường hợp của tôi) là bằng saml (trích dẫn bên dưới).

Mảng chỉ có thể có chỉ mục số. Bạn cần phải viết lại nó dưới dạng Đối tượng hoặc Mảng đối tượng.

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

Nếu bạn thích status.pushphương pháp này, bạn có thể sắp xếp nó bằng:

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

Thông minh! Việc cần làm và sắp xếp alpha theo tên: return a.name> b.name.
mpemburn vào

1
Đối với sắp xếp theo bảng chữ cái, hãy so sánh các giá trị chuỗi trong cùng một trường hợp vì sort()xử lý chúng khác nhau. Nó khiến tôi bối rối trong một giờ cho đến khi tôi tìm thấy ví dụ này stackoverflow.com/questions/6712034/… ; a.name.toLowerCase() > b.name.toLowerCase()
Dylan Valade

5

Thực sự không có bất kỳ thứ gì gọi là "mảng kết hợp" trong JavaScript. Những gì bạn có ở đó chỉ là một đồ vật cũ đơn thuần. Tất nhiên, chúng hoạt động giống như mảng kết hợp và các khóa có sẵn nhưng không có ngữ nghĩa xung quanh thứ tự của các khóa.

Bạn có thể biến đối tượng của mình thành một mảng các đối tượng (cặp khóa / giá trị) và sắp xếp như sau:

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

Sau đó, bạn sẽ gọi nó bằng một hàm so sánh.


+1 bạn đánh bại tôi với nó. Tôi đã viết một lời giải thích và đoạn mã rất giống nhau.
gonchuki

4

Đây là một biến thể của câu trả lời của ben blank, nếu bạn không thích bộ giá trị.

Điều này giúp bạn tiết kiệm một vài ký tự.

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

Không cần phức tạp không cần thiết ...

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

Đầu ra của điều này là một mảng các mảng, không phải một bản đồ
Daniel

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

Tôi nghĩ điều này là sạch sẽ nhất. Chỉ cần thêm chuyển đổi vào bản đồ ở cuối
Daniel

1

tôi sử dụng $ .each của jquery nhưng bạn có thể tạo nó bằng vòng lặp for, một cải tiến là:

        //.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;
              }               
     };

Vì vậy, bây giờ bạn có thể làm

    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

Theo tôi, cách tiếp cận tốt nhất cho trường hợp cụ thể ở đây là một điểm chung được đề xuất. Một cải tiến nhỏ mà tôi muốn đề xuất có thể hoạt động trong các trình duyệt hiện đại là:

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

Điều này có thể áp dụng dễ dàng và hoạt động tốt trong trường hợp cụ thể ở đây, do đó bạn có thể làm:

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

Bên cạnh đó, tôi cung cấp ở đây một hàm "chung chung" hơn mà bạn có thể sử dụng để sắp xếp ngay cả trong nhiều tình huống hơn và kết hợp cải tiến mà tôi vừa đề xuất với các phương pháp tiếp cận câu trả lời của Ben Blank (sắp xếp cả giá trị chuỗi) và PopeJohnPaulII ( sắp xếp theo trường / thuộc tính đối tượng cụ thể) và cho phép bạn quyết định xem bạn muốn một thứ tự con cháu hay con cháu, đây là:

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

Bạn có thể kiểm tra các chức năng bằng cách thử một cái gì đó như mã sau:

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

Như tôi đã nói, bạn có thể lặp lại các phím được sắp xếp nếu bạn cần thực hiện các công việc

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

Cuối cùng, nhưng không kém phần quan trọng, một số tham chiếu hữu ích đến Object.keysArray.sort


0

Vì vậy, nó ở ngoài kia và ai đó đang tìm kiếm các loại dựa trên tuple. Thao tác này sẽ so sánh phần tử đầu tiên của đối tượng trong mảng, với phần tử thứ hai, v.v. tức là trong ví dụ dưới đây, nó sẽ so sánh đầu tiên bởi "a", sau đó là "b" và v.v.

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

KHỞI ĐỘNG

 [ { 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

Câu trả lời của @ commonpike là "đúng", nhưng khi anh ấy tiếp tục nhận xét ...

hầu hết các trình duyệt hiện nay chỉ hỗ trợ Object.keys()

Yeah .. Object.keys()WAY tốt hơn .

Nhưng điều gì thậm chí còn tốt hơn ? Duh, nó vào coffeescriptrồi!

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

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.