簡體   English   中英

按屬性或函數值對javascript數組排序

[英]Sort javascript array by property or function value

我有一個包含原始值和計算值的數組。 我希望能夠基於原始值或計算值之一的結果對數組進行動態排序。 直到運行時,才知道所需的實際排序。

我整理了下面的示例( 此處為 plunker),以演示這種情況和可行的解決方案*。 我想知道如何改善這一點...具體來說,是:

Array.prototype.sortBy = function (property) {
    return this.sort(mySort(property));
};

stackoverflow響應中復制-EgeÖzcan特別聲明

//Please don't just copy-paste this code. 
//See the explanation at the end. A lot could break.

我想了解如何在我的對象上實現這種排序算法,而又不違反“很多可能會中斷”的警告(我不理解)。

*我最喜歡stackoverflow的一件事是,很好地構架問題的過程經常使您將問題簡化到某種程度(不一定 )的解決方案。 我開始無法根據屬性或計算值進行排序的問題。 現在,我正在尋找對實現的驗證/改進。

樣品

var rawData = [
  { "Id": 3, "itemCount": 3531, "val1": 905, "val2": 172 },
  { "Id": 2, "itemCount": 3111, "val1": 799, "val2": 147 },
  { "Id": 4, "itemCount": 3411, "val1": 871, "val2": 199 },
  { "Id": 5, "itemCount": 3414, "val1": 892, "val2": 178 },
  { "Id": 1, "itemCount": 3182, "val1": 845, "val2": 155 }
];



function MyItem(item) {
    var self = this;
    for (var val in item) {
      if (item.hasOwnProperty(val)) {
          self[val] = item[val];
      }
    }
 }

function extendMyItems() {
    MyItem.prototype.computedOne = function () {
        var input = this;
        return input.itemCount / input.val1;
    };

    MyItem.prototype.computedTwo = function () {
        var input = this;
        return input.val1 * input.val2;
    };
}

function getItems(input) {
    var ret = [];
    for (var i = 0; i < input.length; i++) {
        var item = new MyItem(input[i]);
        ret.push(item);
    }

    return ret;
}

function doIt() {

    Array.prototype.sortBy = function (property) {
        return this.sort(mySort(property));
    };

    extendMyItems();
    var sortList = [{ "sortKey": "Id", "sortOrder": "asc" },
                { "sortKey": "val1", "sortOrder": "asc" },
                { "sortKey": "val2", "sortOrder": "desc" },
                { "sortKey": "computedOne", "sortOrder": "desc", "isComputed": true },
                { "sortKey": "Id", "sortOrder": "desc" },
                { "sortKey": "computedTwo", "sortOrder": "asc", "isComputed": true }];

    // get the array of MyItem
    var myItems = getItems(rawData);

    for (var k = 0; k < sortList.length; k++) {
        myItems.sortBy(sortList[k]);
        // process the sorted items here (ranking/scoring, etc)
        for (var p = 0; p < myItems.length; p++) {
            console.log('Id: ' + myItems[p].Id + ' val1: ' + myItems[p].val1 + ' val2:                                ' + myItems[p].val2 + ' c1: ' + myItems[p].computedOne() + ' c2: ' +  myItems[p].computedTwo());
    }

}

function mySort(srt) {
    var so = srt.sortOrder == 'asc' ? 1 : -1;
    var key = srt.sortKey;
    var result = 0;
    console.log(srt.sortKey + ' ' + srt.sortOrder + ':');

    return function (a, b) {
        if (srt.isComputed) {
            // this seems like a hack - is there a better way to switch between property and function value????
            result = (a[key]() < b[key]()) ? -1 : (a[key]() > b[key]()) ? 1 : 0;
        } else {
            result = (a[key] < b[key]) ? -1 : (a[key] > b[key]) ? 1 : 0;
        }

        return result * so;
    };

    }
}

擴展諸如Array類的本機對象被認為是最糟糕的做法。 那就是您提到的帖子。 問題是您不知道這將如何影響其他腳本的行為。

這是一個例子的例子,一旦操作數組原型,寫得不好就會引起問題。 我曾經在大型代碼的基礎上遇到過這種情況,但確實很難追蹤:

function BadUseOfForLoop(){
   //You should NEVER use a for in loop to iterate over an array 
   //although some people do this and it works until you extend Array
   var arr = [1,2,3,4];
    for (key in arr){
      console.log(arr[key]);
    }  
}

BadUseOfForLoop();

console.log("Extend Array...");

Array.prototype.sortBy = function(){
   return "Doesnt matter...";
};

BadUseOfForLoop();

輸出:

1 
2 
3
4 
Extend Array... 
1 
2 
3 
4 
function (){
   return "Doesnt matter...";
} 

http://jsfiddle.net/vTwRY/

為避免警告,您可以做的一件事就是根本不擴展Array對象並創建一個幫助程序來幫助您。

var ArrayHelper = {
    sortBy : function(arr, prop){
        return function(){
            var so = srt.sortOrder == 'asc' ? 1 : -1;
            var key = srt.sortKey;
            var result = 0;
            console.log(srt.sortKey + ' ' + srt.sortOrder + ':');
            return function (a, b) {
                if (srt.isComputed) {
                result = (a[key]() < b[key]()) ? -1 : (a[key]() > b[key]()) ? 1 : 0;
                } else {
                    result = (a[key] < b[key]) ? -1 : (a[key] > b[key]) ? 1 : 0;
                }
                return result * so;
            };
        }
    }
};

然后在您的代碼中...

ArrayHelper.sortBy(myItems,sortList[k]);

代替

myItems.sortBy(sortList[k]);

工作演示

為了進一步閱讀該主題,《 Perfect Perfect Kills》一文討論了擴展原生對象是否是一個好主意,您會發現這不是一成不變的問題。 上面,我提出了一個問題,該問題在本博客文章中未討論,但實際上可能導致與其他代碼的沖突。

答案表明您不應擴展Array.prototype東西。 就我個人而言,我不認為這是致命的罪行; 大多數庫都可以安全地處理這些情況。

也就是說,您可以包裝排序功能:

function ArraySorter(arr)
{
    this.arr = arr;
}

ArraySorter.prototype.sortBy = function(prop) {
    return this.arr.sort(mySort(prop));
}

var sortedItems = new ArraySorter(myItems).sortBy('whatever');

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM