İlişkilendirilebilir bir diziyi Javascript'teki değerlerine göre nasıl sıralayabilirim?


84

İlişkilendirilebilir diziye sahibim:

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

Sonuç, bu sırayla ilgili indislere sahip bir dizi olduğunda değerlerine göre sıralamanın (azalan) en zarif yolu nedir:

sub2, sub3, sub1, sub4, sub0

1
Nesne özelliklerinin dil tanımlı sıralaması olmadığı için - yapamazsınız (bazı JS motorlarında, özellikleri uyguladıkları özel yönteme bağlı olarak belki hariç).
Quentin

Yanıtlar:


115

Javascript sizin düşündüğünüz gibi "ilişkilendirilebilir dizilere" sahip değildir. Bunun yerine, dizi benzeri sözdizimini (örneğinizde olduğu gibi) kullanarak nesne özelliklerini ayarlama ve ayrıca bir nesnenin özelliklerini yineleme yeteneğine sahip olursunuz.

Bunun neticesi , özellikleri yinelediğiniz sırayla ilgili hiçbir garanti olmamasıdır , bu nedenle onlar için bir sıralama gibisi yoktur. Bunun yerine, nesne özelliklerinizi bir "gerçek" diziye dönüştürmeniz gerekir (bu, düzeni garanti eder). Burada, bir nesneyi iki parçadan oluşan bir diziye (iki öğeli diziler) dönüştürmek, onu tanımladığınız gibi sıralamak ve ardından üzerinde yinelemek için bir kod parçacığı verilmiştir:

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
}

Bunu, geri arama alan bir işlevle sarmayı daha doğal bulabilirsiniz:

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 işlevi tuples.sort'a kadar temizlenebilir (function (a, b) {return a [1] - b [1];});
STOT

2
@stot - Değerlerinizin tümü sayı ise (soran kişinin örneğindeki gibi), kesinlikle. Ben de dalgınlıkla dizelerle de çalışan bir karşılaştırma işlevi sağladım. :-)
Ben Blank

@Ben: evet haklısınız, sunulan sürüm daha genel ;-)
durdu

@Ben, bu yazı için çok teşekkür ederim. Dize karşılaştırma işlevi ile ilgili olarak, bir ASCII değer karşılaştırması kullanıyor mu?
jjwdesign

@jjwdesign, dizenin kodlanmamış olduğunu varsayarak, Unicode kod noktasına göre sıralayacaktır.
Ben Blank

86

Sizi bir 'çağrışımsal dizinin' anlambilimini düzeltmek yerine, istediğiniz şeyin bu olduğunu düşünüyorum:

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

için gerçekten eski tarayıcılarda yerine bunu kullanın:

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

Bir nesneyi (sizinki gibi) içine atarsınız ve eh, nesnenin, eh, değerlerinin (sayısal) değerine göre azalan şekilde sıralanmış bir anahtar dizisi - eh özellikleri - geri alırsınız.

Bu sadece değerleriniz sayısal ise işe yarar. Küçük Tweekfunction(a,b)Sıralama mekanizmasını artan şekilde veya stringdeğerler için çalışmak üzere değiştirmek için oradaki kullanın (örneğin). Okuyucu için bir egzersiz olarak bırakıldı.


1
Bu mükemmel çalışıyor ve yukarıdaki cevaptan daha az kodlu
jshill103

1
Günümüzde çoğu tarayıcının yalnızca Object.keys () - developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
commonpike

object.keys neden daha iyi?
john ktejik

1
@johnktejik bu cevap 2012'den alınmıştır :-) Object.keys () günümüzde bir standarttır ve daha kısa ve muhtemelen daha hızlıdır.
commonpike

2
... :) Yorgundum, bunu
manu

16

Bir (ilişkisel) dizi değere göre nasıl sıralanır? Bölümünde anlatılan devam eden tartışma ve diğer çözümler en iyi çözüm (benim durumum için) saml tarafından (aşağıda alıntılanmıştır).

Dizilerin yalnızca sayısal dizinleri olabilir. Bunu bir Nesne veya Nesne Dizisi olarak yeniden yazmanız gerekir.

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.pushYöntemi beğendiyseniz , şu şekilde sıralayabilirsiniz:

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

Mükemmel! Ada göre yapmak ve alfa sıralamak için: a.name> b.name döndür.
mpemburn

1
Alfabetik sıralama için, aynı durumdaki dize değerlerini karşılaştırın, çünkü sort()onlara farklı davranır. Bu stackoverflow.com/questions/6712034/… bulana kadar beni bir saat karıştırdı . a.name.toLowerCase() > b.name.toLowerCase()
Dylan Valade

5

JavaScript'te "ilişkilendirilebilir dizi" diye bir şey gerçekten yoktur. Elinizde olan sadece eski bir nesne. Elbette, bir tür ilişkisel diziler gibi çalışırlar ve anahtarlar mevcuttur, ancak anahtarların sırasına ilişkin anlambilim yoktur.

Nesnenizi bir dizi nesneye (anahtar / değer çiftleri) dönüştürebilir ve şunları sıralayabilirsiniz:

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

Sonra bunu bir karşılaştırma işlevi ile çağırırsınız.


+1 sen beni yendin. Çok benzer bir açıklama ve kod yazıyordum.
gonchuki

4

Tuple'lardan hoşlanmıyorsanız, ben blank'ın cevabının bir varyasyonu.

Bu size birkaç karakter kazandırır.

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

Gereksiz komplikasyona gerek yok ...

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

Bunun çıktısı bir dizi dizisidir, harita değil
Daniel

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

Bunun en temiz olduğunu düşünüyorum. Haritanın sonunda bir dönüşüm eklemeniz yeterli
Daniel

1

$ .each jquery kullanıyorum ama bunu bir for döngüsü ile yapabilirsiniz, bir iyileştirme şudur:

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

Yani şimdi yapabilirsin

    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

Buradaki özel durum için en iyi yaklaşım, bence, önerilen bir ortak yaklaşımdır . Modern tarayıcılarda işe yarayan küçük bir iyileştirme:

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

Bu, kolayca uygulanabilir ve buradaki özel durumda harika çalışabilir, böylece şunları yapabilirsiniz:

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

Bunun yanı sıra, burada daha geniş bir durumda bile sıralama için kullanabileceğiniz daha "genel" bir işlev sunuyorum ve bu, önerdiğim iyileştirmeyi Ben Blank (aynı zamanda dize değerlerini de sıralamak) ve PopeJohnPaulII'nin ( belirli bir nesne alanına / özelliğine göre sıralama) ve artan veya azalan bir sıra isteyip istemediğinize karar vermenizi sağlar, işte burada:

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

Aşağıdaki kod gibi bir şeyi deneyerek işlevleri test edebilirsiniz:

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

Daha önce de söylediğim gibi, bir şeyler yapmanız gerekiyorsa sıralı anahtarların üzerinden geçebilirsiniz.

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

Son olarak, Object.keys ve Array.sort için bazı yararlı referanslar


0

Sadece oradadır ve birisi demet tabanlı sıralama arıyor. Bu, dizideki nesnenin ilk öğesini ikinci öğeden vb. Karşılaştırır. yani aşağıdaki örnekte, önce "a" ile, sonra "b" ile vb. karşılaştırılacaktır.

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

ÇIKIŞLAR

 [ { 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'ın cevabı "doğru olan", ancak yorum yapmaya devam ettikçe ...

günümüzde çoğu tarayıcı yalnızca Object.keys()

Evet .. Object.keys()olduğunu daha iyi YOL .

Ama daha iyi olan ne ? Duh, içeri girdi 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' ]

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.