[javascript] 여러 필드로 객체 배열을 정렬하는 방법은 무엇입니까?

원래 질문 에서 여러 필드에 정렬을 어떻게 적용합니까?

이 약간 적응 된 구조를 사용하여 도시를 오름차순으로 정렬 한 다음 가격을 내림차순으로 어떻게 정렬합니까?

var homes = [
    {"h_id":"3",
     "city":"Dallas",
     "state":"TX",
     "zip":"75201",
     "price":"162500"},
    {"h_id":"4",
     "city":"Bevery Hills",
     "state":"CA",
     "zip":"90210",
     "price":"319250"},
    {"h_id":"6",
     "city":"Dallas",
     "state":"TX",
     "zip":"75000",
     "price":"556699"},
    {"h_id":"5",
     "city":"New York",
     "state":"NY",
     "zip":"00010",
     "price":"962500"}
    ];

나는 일반적인 접근 방식을 제공 한 답변 보다 사실을 좋아했습니다 . 이 코드를 사용할 계획이라면 날짜뿐만 아니라 다른 것들도 정렬해야합니다. 조금 번거롭지 않더라도 객체를 “프라이밍”하는 기능은 편리해 보였습니다.

나는이 답변 을 좋은 일반적인 예로 만들려고 노력했지만 운이 좋지 않습니다.



답변

이 답변을 기반으로하는 다차원 정렬 방법 :

업데이트 : 여기에 “최적화 된”버전이 있습니다. 더 많은 사전 처리를 수행하고 사전에 각 정렬 옵션에 대한 비교 기능을 만듭니다. 더 많은 메모리가 필요할 수 있습니다 (각 정렬 옵션에 대한 기능을 저장하기는하지만 비교 중에 올바른 설정을 결정할 필요가 없으므로 조금 더 나은 성능을 유지해야합니다).

var sort_by;

(function() {
    // utility functions
    var default_cmp = function(a, b) {
            if (a == b) return 0;
            return a < b ? -1 : 1;
        },
        getCmpFunc = function(primer, reverse) {
            var dfc = default_cmp, // closer in scope
                cmp = default_cmp;
            if (primer) {
                cmp = function(a, b) {
                    return dfc(primer(a), primer(b));
                };
            }
            if (reverse) {
                return function(a, b) {
                    return -1 * cmp(a, b);
                };
            }
            return cmp;
        };

    // actual implementation
    sort_by = function() {
        var fields = [],
            n_fields = arguments.length,
            field, name, reverse, cmp;

        // preprocess sorting options
        for (var i = 0; i < n_fields; i++) {
            field = arguments[i];
            if (typeof field === 'string') {
                name = field;
                cmp = default_cmp;
            }
            else {
                name = field.name;
                cmp = getCmpFunc(field.primer, field.reverse);
            }
            fields.push({
                name: name,
                cmp: cmp
            });
        }

        // final comparison function
        return function(A, B) {
            var a, b, name, result;
            for (var i = 0; i < n_fields; i++) {
                result = 0;
                field = fields[i];
                name = field.name;

                result = field.cmp(A[name], B[name]);
                if (result !== 0) break;
            }
            return result;
        }
    }
}());

사용법 예 :

homes.sort(sort_by('city', {name:'price', primer: parseInt, reverse: true}));

데모


원래 기능 :

var sort_by = function() {
   var fields = [].slice.call(arguments),
       n_fields = fields.length;

   return function(A,B) {
       var a, b, field, key, primer, reverse, result, i;

       for(i = 0; i < n_fields; i++) {
           result = 0;
           field = fields[i];

           key = typeof field === 'string' ? field : field.name;

           a = A[key];
           b = B[key];

           if (typeof field.primer  !== 'undefined'){
               a = field.primer(a);
               b = field.primer(b);
           }

           reverse = (field.reverse) ? -1 : 1;

           if (a<b) result = reverse * -1;
           if (a>b) result = reverse * 1;
           if(result !== 0) break;
       }
       return result;
   }
};

데모


답변

정확한 문제에 대한 제네릭이 아닌 간단한 솔루션 :

homes.sort(
   function(a, b) {
      if (a.city === b.city) {
         // Price is only important when cities are the same
         return b.price - a.price;
      }
      return a.city > b.city ? 1 : -1;
   });


답변

0이 아닌 값에 도달 할 때까지 값 델타를 가져 와서 체인 정렬 방식을 사용할 수 있습니다.

var data = [{ h_id: "3", city: "Dallas", state: "TX", zip: "75201", price: "162500" }, { h_id: "4", city: "Bevery Hills", state: "CA", zip: "90210", price: "319250" }, { h_id: "6", city: "Dallas", state: "TX", zip: "75000", price: "556699" }, { h_id: "5", city: "New York", state: "NY", zip: "00010", price: "962500" }];

data.sort(function (a, b) {
    return a.city.localeCompare(b.city) || b.price - a.price;
});

console.log(data);
.as-console-wrapper { max-height: 100% !important; top: 0; }

또는 es6을 사용하여 간단히 :

data.sort((a, b) => a.city.localeCompare(b.city) || b.price - a.price);


답변

다음은 간단한 기능적 접근 방식입니다. 배열을 사용하여 정렬 순서를 지정하십시오. 내림차순을 지정하려면 빼기 부호 를 붙 입니다.

var homes = [
    {"h_id":"3", "city":"Dallas", "state":"TX","zip":"75201","price":"162500"},
    {"h_id":"4","city":"Bevery Hills", "state":"CA", "zip":"90210", "price":"319250"},
    {"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":"556699"},
    {"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":"962500"}
    ];

homes.sort(fieldSorter(['city', '-price']));
// homes.sort(fieldSorter(['zip', '-state', 'price'])); // alternative

function fieldSorter(fields) {
    return function (a, b) {
        return fields
            .map(function (o) {
                var dir = 1;
                if (o[0] === '-') {
                   dir = -1;
                   o=o.substring(1);
                }
                if (a[o] > b[o]) return dir;
                if (a[o] < b[o]) return -(dir);
                return 0;
            })
            .reduce(function firstNonZeroValue (p,n) {
                return p ? p : n;
            }, 0);
    };
}

편집 : ES6에서는 훨씬 짧습니다!

"use strict";
const fieldSorter = (fields) => (a, b) => fields.map(o => {
    let dir = 1;
    if (o[0] === '-') { dir = -1; o=o.substring(1); }
    return a[o] > b[o] ? dir : a[o] < b[o] ? -(dir) : 0;
}).reduce((p, n) => p ? p : n, 0);

const homes = [{"h_id":"3", "city":"Dallas", "state":"TX","zip":"75201","price":162500},     {"h_id":"4","city":"Bevery Hills", "state":"CA", "zip":"90210", "price":319250},{"h_id":"6", "city":"Dallas", "state":"TX", "zip":"75000", "price":556699},{"h_id":"5", "city":"New York", "state":"NY", "zip":"00010", "price":962500}];
const sortedHomes = homes.sort(fieldSorter(['state', '-price']));

document.write('<pre>' + JSON.stringify(sortedHomes, null, '\t') + '</pre>')


답변

나는 오늘 매우 일반적인 다중 기능 분류기를 만들었습니다. thenBy.js를 여기에서 볼 수 있습니다 : https://github.com/Teun/thenBy.js

표준 Array.sort를 사용할 수 있지만 firstBy (). thenBy (). thenBy () 스타일을 사용할 수 있습니다. 위에 게시 된 솔루션보다 코드와 복잡성이 적습니다.


답변

다음 함수를 사용하면 오름차순 (기본값) 또는 각 속성에서 내림차순으로 하나 이상의 속성에서 객체 배열을 정렬 할 수 있으며 대소 문자 구분 비교를 수행할지 여부를 선택할 수 있습니다. 기본적으로이 함수는 대소 문자를 구분하지 않는 정렬을 수행합니다.

첫 번째 인수는 객체를 포함하는 배열이어야합니다. 후속 인수는 정렬 할 다른 객체 속성을 참조하는 쉼표로 구분 된 문자열 목록이어야합니다. 마지막 인수 (선택 사항)는 대소 문자 구분 정렬을 수행할지 여부를 선택하는 부울 true입니다 (대소 문자 구분 정렬에 사용).

이 기능은 기본적으로 각 속성 / 키를 오름차순으로 정렬합니다. 특정 키를 내림차순으로 정렬하려면 대신 다음 형식으로 배열을 전달하십시오 ['property_name', true].

다음은 함수의 샘플 사용과 설명 ( homes객체를 포함하는 배열)입니다.

objSort(homes, 'city') -> 도시별로 정렬 (오름차순, 대소 문자 구분)

objSort(homes, ['city', true]) -> 도시별로 정렬 (내림차순, 대소 문자 구분)

objSort(homes, 'city', true)-> 도시별로 정렬 한 다음 가격 (오름차순, 대소 문자 구분 )

objSort(homes, 'city', 'price') -> 도시별로 정렬 한 다음 가격 (오름차순, 대소 문자 구분)

objSort(homes, 'city', ['price', true]) -> 도시별로 정렬 (오름차순), 가격 (내림차순), 대소 문자 구분)

그리고 더 이상 고민하지 않고 기능은 다음과 같습니다.

function objSort() {
    var args = arguments,
        array = args[0],
        case_sensitive, keys_length, key, desc, a, b, i;

    if (typeof arguments[arguments.length - 1] === 'boolean') {
        case_sensitive = arguments[arguments.length - 1];
        keys_length = arguments.length - 1;
    } else {
        case_sensitive = false;
        keys_length = arguments.length;
    }

    return array.sort(function (obj1, obj2) {
        for (i = 1; i < keys_length; i++) {
            key = args[i];
            if (typeof key !== 'string') {
                desc = key[1];
                key = key[0];
                a = obj1[args[i][0]];
                b = obj2[args[i][0]];
            } else {
                desc = false;
                a = obj1[args[i]];
                b = obj2[args[i]];
            }

            if (case_sensitive === false && typeof a === 'string') {
                a = a.toLowerCase();
                b = b.toLowerCase();
            }

            if (! desc) {
                if (a < b) return -1;
                if (a > b) return 1;
            } else {
                if (a > b) return -1;
                if (a < b) return 1;
            }
        }
        return 0;
    });
} //end of objSort() function

샘플 데이터는 다음과 같습니다.

var homes = [{
    "h_id": "3",
    "city": "Dallas",
    "state": "TX",
    "zip": "75201",
    "price": 162500
}, {
    "h_id": "4",
    "city": "Bevery Hills",
    "state": "CA",
    "zip": "90210",
    "price": 1000000
}, {
    "h_id": "5",
    "city": "new york",
    "state": "NY",
    "zip": "00010",
    "price": 1000000
}, {
    "h_id": "6",
    "city": "Dallas",
    "state": "TX",
    "zip": "85000",
    "price": 300000
}, {
    "h_id": "7",
    "city": "New York",
    "state": "NY",
    "zip": "00020",
    "price": 345000
}];


답변

이것은 완전한 속임수이지만 기본적으로 사용할 수있는 통조림 라이브러리 기능이기 때문에이 질문에 가치를 더한다고 생각합니다.

코드가 lodashlodash 호환 라이브러리에 액세스 할 수 있는 경우이 메소드를 underscore사용할 수 있습니다 _.sortBy. 아래 스 니펫은 lodash 설명서 에서 직접 복사됩니다 .

예제에서 주석 처리 된 결과는 배열의 배열을 반환하는 것처럼 보이지만 객체의 배열 인 실제 결과가 아닌 순서를 보여줍니다.

var users = [
  { 'user': 'fred',   'age': 48 },
  { 'user': 'barney', 'age': 36 },
  { 'user': 'fred',   'age': 40 },
  { 'user': 'barney', 'age': 34 }
];

_.sortBy(users, [function(o) { return o.user; }]);
 // => objects for [['barney', 36], ['barney', 34], ['fred', 48], ['fred', 40]]

_.sortBy(users, ['user', 'age']);
// => objects for [['barney', 34], ['barney', 36], ['fred', 40], ['fred', 48]]