2013-07-03 3 views
0

У меня есть массив, содержащий исходные значения, а также вычисленные значения. Я хотел бы иметь возможность сортировать массив динамически на основе либо необработанного значения, либо результата одного из вычисленных значений. Фактические виды, которые потребуются, не будут известны до времени выполнения.Сортировка массива javascript по значению или значению функции

Я собрал образец ниже (plunker here), который демонстрирует ситуацию и рабочее решение *. Я хотел бы знать, как улучшить это ... В частности, использование:

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

копируется из ответа this StackOverflow - и Эге Ö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; 
    }; 

    } 
} 
+0

Реализация выглядит хорошо; в чем именно проблема? –

+0

Привет, Джек, просто интересно, если 'Array.prototype.sortBy' является правильным способом для этого ... Я новичок в javascript, поэтому я не понял, что« Множество может сломать »предупреждение из того, что я скопировал/вставить. – Kevin

+0

См. [Почему используется «для ... в» с итерацией массива такая плохая идея?] (Http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-such -а-плохая идея /) о том, что может сломаться. И ответ, который вы связали, уже объясняет, как обойти это. – Bergi

ответ

0

В ответе говорится, что вы не должны распространять такие вещи, как 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'); 
1

Это, по мнению большинства плохая практика, чтобы расширить собственные объекты, такие как 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/

Одна вещь, которую вы могли бы сделать, чтобы избежать caveat просто не расширяет объект 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]); 

Working demo

Для дальнейшего чтения по этому вопросу этот Perfection Kills пост обсуждает ли не распространяющие нативные объекты - хорошая идея, и вы увидите, что это не и сухой вопрос. Выше я изложил проблему, которая не обсуждалась в этом сообщении в блоге, но на самом деле может вызвать конфликты с другим кодом.

+0

marteljn - рискуя звучать как идиот, я не знаю, как это сделать. Мне нравится, как вы идете ... можете ли вы показать мне, как реализовать свое предложение? – Kevin

+0

Да, я подробно расскажу в ответе после обеда. – marteljn

+0

@ Кевин. Я применил ваш вид так, как я предлагал. Функционально он делает то же самое, что и вы, но так, что позволяет избежать возможных проблем с распространением собственных объектов. – marteljn

Смежные вопросы