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


147

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

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

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

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

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


검색 또는 정렬 하시겠습니까?
Felix Kling

연결 한 두 번째 답변을 사용하면서 발생한 문제는 정확히 무엇입니까?
캐논

충분하지 않습니다. 간단히 말하고 싶을 때 코드를 추가하는 것 같습니다. sort(["first-field", "ASC"], ["second-field", "DSC"]); 날짜, 대소 문자 구분 등을 처리 할 수 ​​있도록 첫 번째 답변의 "primer"논리를 추가하려고 할 때 더욱 복잡합니다.
Mike

또는 당신은 각 필드에 가중치를 적용 할 수 있습니다
onmyway133

lodash를 사용해도 괜찮다면 lodash.com/docs/4.17.11#orderBy 를 확인할 수 있습니다
Deepanshu Arora

답변:


83

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

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

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

데모


2
레코드의 경우이 함수는 인수 목록을 사전 처리하고 균일 한 "정렬 옵션 배열"을 작성하여 개선 할 수 있습니다. 이것은 독자를위한 연습으로 남겨두고;)
Felix Kling

@Mike : Ok ... finally;) 옵션이 사전 처리되므로 더 복잡해 보이지만 최종 비교 기능 (주석 참조)이 훨씬 단순하여 성능 향상을 가져올 수 있습니다. 정렬 옵션이 많을수록이 방법에서 더 많은 이점을 얻을 수 있습니다.
Felix Kling

165

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

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

6
나는이 데모는 영업 이익 => 원하는 것을 생각 jsfiddle.net/zJ6UA/533
아민 Jafari

3
이것은 올바른 생각을 가지고 있지만 논리는 모두 잘못되었습니다. 다른 문자열에서 숫자가 아닌 문자열을 뺄 수 없으며 if명령문은 의미가 없습니다.
JLRishe

6
a.localeCompare(b)문자열 비교를 위해 마지막 줄에서 사용할 수 있습니다 ... 문서 참조
Michael P

2
첫 번째 도시 비교가 불평등이 아닌 평등을 점검해서는 안 되는가? 다시 말해서, 선이 아니어야 if (a.city === b.city)합니까? 즉, 두 도시가 같으면 가격을 비교하고 그렇지 않으면 도시를 비교하십시오.
Steven Rands

2
가장 좋은 답변 중 하나입니다. tnx.
jonathana

54

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

16
뭔가 빠졌습니까? 1에서 할 수있는 일에 60 줄의 코드를 사용해야하는 이유 간단하고 명확하며 간결합니다. 허용되는 답변 IMO 여야합니다.
Erez Cohen

현재 SO의 가장 큰 문제 중 하나는 오래된 답변입니다. 종종 새로운 언어 기능 (예 : ES5-6-7)을 사용하는 더 나은 솔루션으로 대체되어 기존 점수를 유지하며 "실제"최고를 찾기 위해 아래로 스크롤해야합니다. 솔루션! 시간이 지남에 따라 문제가 점점 악화되고 있으므로 SO는 시간이 지남에 따라 투표를 만료해야합니다.
Andy Lorenz

53

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

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


6
이 기능은 매우 깔끔해서 파서에 따라 최대 90 %의 작은 성능 향상을 이루었습니다. 나는 요점테스트 스위트를 만들었습니다 .
php_nub_qq

예상대로 숫자처럼 보이는 샘플 데이터를 기반으로 ... 내가 더 문자열처럼 정렬 곳이 번호를 구현하려고하지만 때, 분류되어 있습니다 [10,100,11,9]. 내가 뭐 놓친 거 없니?
Mark Carpenter Jr

@MarkCarpenterJr. 당신이 무슨 뜻인지 확실하지. 내 예제는 숫자 유형을 올바르게 정렬합니다. 구현을 질문으로 공유하고 의견에서 나를 참조하여 볼 수 있습니까? 그럼 확인할 수 있습니다.
chriskelly

@MarkCarpenterJr. 방금 발견했습니다. 의견에 설명을 추가했습니다.
chriskelly

32

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

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


8
글쎄, 당신이 세 번 전화를 할 때, 두 번째 전화가 차이를 만들지 않는 항목에 대해서는 두 번째 전화가 첫 번째 전화의 순서를 그대로 유지한다고 보장하지 않습니다.
Teun D

13

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

첫 번째 인수는 객체를 포함하는 배열이어야합니다. 후속 인수는 정렬 할 다른 객체 속성을 참조하는 쉼표로 구분 된 문자열 목록이어야합니다. 마지막 인수 (선택 사항)는 대소 문자 구분 정렬을 수행할지 여부를 선택하는 부울 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
}];

8

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

코드가 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]]

7

구문에 대한 귀하의 아이디어에 더 가까운 또 다른 것이 있습니다.

function sortObjects(objArray, properties /*, primers*/) {
    var primers = arguments[2] || {}; // primers are optional

    properties = properties.map(function(prop) {
        if( !(prop instanceof Array) ) {
            prop = [prop, 'asc']
        }
        if( prop[1].toLowerCase() == 'desc' ) {
            prop[1] = -1;
        } else {
            prop[1] = 1;
        }
        return prop;
    });

    function valueCmp(x, y) {
        return x > y ? 1 : x < y ? -1 : 0; 
    }

    function arrayCmp(a, b) {
        var arr1 = [], arr2 = [];
        properties.forEach(function(prop) {
            var aValue = a[prop[0]],
                bValue = b[prop[0]];
            if( typeof primers[prop[0]] != 'undefined' ) {
                aValue = primers[prop[0]](aValue);
                bValue = primers[prop[0]](bValue);
            }
            arr1.push( prop[1] * valueCmp(aValue, bValue) );
            arr2.push( prop[1] * valueCmp(bValue, aValue) );
        });
        return arr1 < arr2 ? -1 : 1;
    }

    objArray.sort(function(a, b) {
        return arrayCmp(a, b);
    });
}

// just for fun use this to reverse the city name when sorting
function demoPrimer(str) {
    return str.split('').reverse().join('');
}

// Example
sortObjects(homes, ['city', ['price', 'desc']], {city: demoPrimer});

데모 : http://jsfiddle.net/Nq4dk/2/


편집 : 재미를 위해 여기 에 SQL과 같은 문자열을 취하는 변형 이 있으므로 수행 할 수 있습니다sortObjects(homes, "city, price desc")

function sortObjects(objArray, properties /*, primers*/) {
    var primers = arguments[2] || {};

    properties = properties.split(/\s*,\s*/).map(function(prop) {
        prop = prop.match(/^([^\s]+)(\s*desc)?/i);
        if( prop[2] && prop[2].toLowerCase() === 'desc' ) {
            return [prop[1] , -1];
        } else {
            return [prop[1] , 1];
        }
    });

    function valueCmp(x, y) {
        return x > y ? 1 : x < y ? -1 : 0; 
    }

    function arrayCmp(a, b) {
        var arr1 = [], arr2 = [];
        properties.forEach(function(prop) {
            var aValue = a[prop[0]],
                bValue = b[prop[0]];
            if( typeof primers[prop[0]] != 'undefined' ) {
                aValue = primers[prop[0]](aValue);
                bValue = primers[prop[0]](bValue);
            }
            arr1.push( prop[1] * valueCmp(aValue, bValue) );
            arr2.push( prop[1] * valueCmp(bValue, aValue) );
        });
        return arr1 < arr2 ? -1 : 1;
    }

    objArray.sort(function(a, b) {
        return arrayCmp(a, b);
    });
}

이 솔루션은 배열 비교 때문에 깨끗하지만 성능이 좋지 않습니다. 단순히 속성을 통해 비교 값을 추적하고 0이 아닌 값을 반환 할 수 있습니다. 훨씬 빠릅니다.
amankapur91

4

더 간단한 것 :

var someArray = [...];

function generateSortFn(props) {
    return function (a, b) {
        for (var i = 0; i < props.length; i++) {
            var prop = props[i];
            var name = prop.name;
            var reverse = prop.reverse;
            if (a[name] < b[name])
                return reverse ? 1 : -1;
            if (a[name] > b[name])
                return reverse ? -1 : 1;
        }
        return 0;
    };
};

someArray.sort(generateSortFn([{name: 'prop1', reverse: true}, {name: 'prop2'}]));

3

나는 SnowBurnt의 접근 방식을 좋아하지만 차이가없는 도시의 동등성을 테스트하기 위해 조정이 필요합니다.

homes.sort(
   function(a,b){
      if (a.city==b.city){
         return (b.price-a.price);
      } else {
         return (a.city-b.city);
      }
   });

3

다음은 일반적인 다차원 정렬입니다. 각 레벨에서 반전 및 / 또는 매핑이 가능합니다.

Typescript로 작성되었습니다. Javascript의 경우이 JSFiddle을 확인하십시오.

코드

type itemMap = (n: any) => any;

interface SortConfig<T> {
  key: keyof T;
  reverse?: boolean;
  map?: itemMap;
}

export function byObjectValues<T extends object>(keys: ((keyof T) | SortConfig<T>)[]): (a: T, b: T) => 0 | 1 | -1 {
  return function(a: T, b: T) {
    const firstKey: keyof T | SortConfig<T> = keys[0];
    const isSimple = typeof firstKey === 'string';
    const key: keyof T = isSimple ? (firstKey as keyof T) : (firstKey as SortConfig<T>).key;
    const reverse: boolean = isSimple ? false : !!(firstKey as SortConfig<T>).reverse;
    const map: itemMap | null = isSimple ? null : (firstKey as SortConfig<T>).map || null;

    const valA = map ? map(a[key]) : a[key];
    const valB = map ? map(b[key]) : b[key];
    if (valA === valB) {
      if (keys.length === 1) {
        return 0;
      }
      return byObjectValues<T>(keys.slice(1))(a, b);
    }
    if (reverse) {
      return valA > valB ? -1 : 1;
    }
    return valA > valB ? 1 : -1;
  };
}

사용 예

사람들을 성으로 정렬 한 다음 이름으로 정렬 :

interface Person {
  firstName: string;
  lastName: string;
}

people.sort(byObjectValues<Person>(['lastName','firstName']));

언어 코드가 아닌 이름으로 언어 코드를 정렬 map한 다음 (참조 ) 내림차순으로 정렬합니다 (참조 reverse).

interface Language {
  code: string;
  version: number;
}

// languageCodeToName(code) is defined elsewhere in code

languageCodes.sort(byObjectValues<Language>([
  {
    key: 'code',
    map(code:string) => languageCodeToName(code),
  },
  {
    key: 'version',
    reverse: true,
  }
]));

2

MULTIPLE 키를 사용하여 동적으로 수행하는 방법 :

  • 각 열 / 열쇠에서 고유 한 값을 필터링
  • 순서대로 놓거나 뒤집어 놓다
  • indexOf (value) 키 값을 기반으로 각 객체에 가중치 너비 0 패드 추가
  • 계산 된 무게를 사용하여 정렬

여기에 이미지 설명을 입력하십시오

Object.defineProperty(Array.prototype, 'orderBy', {
value: function(sorts) { 
    sorts.map(sort => {            
        sort.uniques = Array.from(
            new Set(this.map(obj => obj[sort.key]))
        );

        sort.uniques = sort.uniques.sort((a, b) => {
            if (typeof a == 'string') {
                return sort.inverse ? b.localeCompare(a) : a.localeCompare(b);
            }
            else if (typeof a == 'number') {
                return sort.inverse ? (a < b) : (a > b ? 1 : 0);
            }
            else if (typeof a == 'boolean') {
                let x = sort.inverse ? (a === b) ? 0 : a? -1 : 1 : (a === b) ? 0 : a? 1 : -1;
                return x;
            }
            return 0;
        });
    });

    const weightOfObject = (obj) => {
        let weight = "";
        sorts.map(sort => {
            let zeropad = `${sort.uniques.length}`.length;
            weight += sort.uniques.indexOf(obj[sort.key]).toString().padStart(zeropad, '0');
        });
        //obj.weight = weight; // if you need to see weights
        return weight;
    }

    this.sort((a, b) => {
        return weightOfObject(a).localeCompare( weightOfObject(b) );
    });

    return this;
}
});

사용하다:

// works with string, number and boolean
let sortered = your_array.orderBy([
    {key: "type", inverse: false}, 
    {key: "title", inverse: false},
    {key: "spot", inverse: false},
    {key: "internal", inverse: true}
]);

여기에 이미지 설명을 입력하십시오


1

@Snowburnt 솔루션의 일반 버전은 다음과 같습니다.

var sortarray = [{field:'city', direction:'asc'}, {field:'price', direction:'desc'}];
array.sort(function(a,b){
    for(var i=0; i<sortarray.length; i++){
        retval = a[sortarray[i].field] < b[sortarray[i].field] ? -1 : a[sortarray[i].field] > b[sortarray[i].field] ? 1 : 0;
        if (sortarray[i].direction == "desc") {
            retval = retval * -1;
        }
        if (retval !== 0) {
            return retval;
        }
    }
}


})

이것은 내가 사용하는 정렬 루틴을 기반으로합니다. 이 특정 코드를 테스트하지 않았으므로 오류가있을 수 있지만 아이디어를 얻습니다. 아이디어는 차이를 나타내는 첫 번째 필드를 기준으로 정렬 한 다음 중지하고 다음 레코드로 이동하는 것입니다. 따라서 세 개의 필드로 정렬하고 비교의 첫 번째 필드로 정렬되는 두 레코드의 정렬 순서를 결정하기에 충분하면 해당 정렬 결과를 반환하고 다음 레코드로 이동하십시오.

5000 레코드에서 (실제로는 좀 더 복잡한 정렬 논리를 사용하여) 테스트했으며 눈을 깜박였습니다. 실제로 클라이언트에 1000 개가 넘는 레코드를로드하는 경우 서버 측 정렬 및 필터링을 사용해야합니다.

이 코드는 대소 문자 구분을 처리하지 않지만이 간단한 수정을 처리하기 위해 독자에게 맡깁니다.


1

다음은 Schwartzian 변환 관용구를 기반으로 한 내 솔루션 입니다. 유용한 정보가 있기를 바랍니다.

function sortByAttribute(array, ...attrs) {
  // generate an array of predicate-objects contains
  // property getter, and descending indicator
  let predicates = attrs.map(pred => {
    let descending = pred.charAt(0) === '-' ? -1 : 1;
    pred = pred.replace(/^-/, '');
    return {
      getter: o => o[pred],
      descend: descending
    };
  });
  // schwartzian transform idiom implementation. aka: "decorate-sort-undecorate"
  return array.map(item => {
    return {
      src: item,
      compareValues: predicates.map(predicate => predicate.getter(item))
    };
  })
  .sort((o1, o2) => {
    let i = -1, result = 0;
    while (++i < predicates.length) {
      if (o1.compareValues[i] < o2.compareValues[i]) result = -1;
      if (o1.compareValues[i] > o2.compareValues[i]) result = 1;
      if (result *= predicates[i].descend) break;
    }
    return result;
  })
  .map(item => item.src);
}

사용 방법의 예는 다음과 같습니다.

let games = [
  { name: 'Pako',              rating: 4.21 },
  { name: 'Hill Climb Racing', rating: 3.88 },
  { name: 'Angry Birds Space', rating: 3.88 },
  { name: 'Badland',           rating: 4.33 }
];

// sort by one attribute
console.log(sortByAttribute(games, 'name'));
// sort by mupltiple attributes
console.log(sortByAttribute(games, '-rating', 'name'));

1
나는 이것과 다른 페이지에서 몇 가지를 시도했다. a8m 의이 솔루션은 내 상황에서 효과가있는 솔루션이었습니다 : gist.github.com/cemerson/f1f1434286c1262b403f3d85c96688e0
Christopher D. Emerson

1

또 다른 방법

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"}
    ];
function sortBy(ar) {
  return ar.sort((a, b) => a.city === b.city ?
      b.price.toString().localeCompare(a.price) :
      a.city.toString().localeCompare(b.city));
}
console.log(sortBy(homes));


0
function sortMultiFields(prop){
    return function(a,b){
        for(i=0;i<prop.length;i++)
        {
            var reg = /^\d+$/;
            var x=1;
            var field1=prop[i];
            if(prop[i].indexOf("-")==0)
            {
                field1=prop[i].substr(1,prop[i].length);
                x=-x;
            }

            if(reg.test(a[field1]))
            {
                a[field1]=parseFloat(a[field1]);
                b[field1]=parseFloat(b[field1]);
            }
            if( a[field1] > b[field1])
                return x;
            else if(a[field1] < b[field1])
                return -x;
        }
    }
}

사용 방법 (내림차순으로 특정 필드를 정렬하려면 필드 앞에-(빼기) 기호를 입력하십시오)

homes.sort(sortMultiFields(["city","-price"]));

위의 함수를 사용하면 여러 필드로 json 배열을 정렬 할 수 있습니다. 함수 본문을 전혀 변경할 필요가 없습니다.


0

@chriskelly의 답변의 적응.


대부분의 답변은 값이 10 만 이상이고 백만 이상이면 가격이 올바르게 정렬되지 않을 것이라고 간과합니다. JS는 알파벳순으로 정렬됩니다. 여기에 꽤 잘 대답했습니다. 왜 JavaScript를 정렬 할 수 없는지 "5, 10, 1"정수 배열을 올바르게 정렬하는 방법 입니다.

우리가 정렬하는 필드 나 노드가 숫자라면 궁극적으로 몇 가지 평가를 수행해야합니다. 나는 parseInt()이 경우에 사용하는 것이 정답 이라고 말하지 않고 정렬 된 결과가 더 중요합니다.

var homes = [{
  "h_id": "2",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "62500"
}, {
  "h_id": "1",
  "city": "Dallas",
  "state": "TX",
  "zip": "75201",
  "price": "62510"
}, {
  "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(['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 (!parseInt(a[o]) && !parseInt(b[o])) {
          if (a[o] > b[o]) return dir;
          if (a[o] < b[o]) return -(dir);
          return 0;
        } else {
          return dir > 0 ? a[o] - b[o] : b[o] - a[o];
        }
      })
      .reduce(function firstNonZeroValue(p, n) {
        return p ? p : n;
      }, 0);
  };
}
document.getElementById("output").innerHTML = '<pre>' + JSON.stringify(homes, null, '\t') + '</pre>';
<div id="output">

</div>


테스트 할 바이올린


문제는 정렬하려는 데이터에 있습니다. price이 예에서 문자열 형식입니다. 내 예제에서 올바르게 작동하려면 map을 사용하여 숫자 형식으로 지정할 필드를 먼저 변환하십시오. 즉const correctedHomes = homes.map(h => ({...h, price: +h.price}))
chriskelly

0

와우, 여기에 복잡한 해결책이 있습니다. 너무 복잡해서 더 단순하면서도 강력한 것을 생각해보기로했습니다. 여기있어;

function sortByPriority(data, priorities) {
  if (priorities.length == 0) {
    return data;
  }

  const nextPriority = priorities[0];
  const remainingPriorities = priorities.slice(1);

  const matched = data.filter(item => item.hasOwnProperty(nextPriority));
  const remainingData = data.filter(item => !item.hasOwnProperty(nextPriority));

  return sortByPriority(matched, remainingPriorities)
    .sort((a, b) => (a[nextPriority] > b[nextPriority]) ? 1 : -1)
    .concat(sortByPriority(remainingData, remainingPriorities));
}

다음은 사용 방법의 예입니다.

const data = [
  { id: 1,                         mediumPriority: 'bbb', lowestPriority: 'ggg' },
  { id: 2, highestPriority: 'bbb', mediumPriority: 'ccc', lowestPriority: 'ggg' },
  { id: 3,                         mediumPriority: 'aaa', lowestPriority: 'ggg' },
];

const priorities = [
  'highestPriority',
  'mediumPriority',
  'lowestPriority'
];


const sorted = sortByPriority(data, priorities);

먼저 속성의 우선 순위를 기준으로 정렬 한 다음 속성 값을 기준으로 정렬합니다.


0

여러 필드를 기준으로 정렬하는 확장 가능한 방법이 있습니다.

homes.sort(function(left, right) {
    var city_order = left.city.localeCompare(right.city);
    var price_order = parseInt(left.price) - parseInt(right.price);
    return city_order || -price_order;
});

노트

  • a.localeCompare(b)되는 보편적 지원 및 반환 -1,0,1 경우 a<b, a==b, a>b각각.
  • 빼기는 숫자 필드에서 작동합니다.
  • ||마지막 줄에서 city보다 우선권을 price줍니다.
  • 다음과 같이 모든 필드에서 순서를 바꾸려면 무효화 -price_order
  • 날짜 비교 , var date_order = new Date(left.date) - new Date(right.date);때문에 수치처럼 작동 날짜 수학은 1970 년 이후 밀리 초 단위로 바뀝니다.
  • or-chain에 필드를 추가하고 return city_order || -price_order || date_order;

0

나는 이것이 가장 쉬운 방법이라고 생각합니다.

https://coderwall.com/p/ebqhca/javascript-sort-by-two-fields

정말 간단하고 3 가지 키 값 쌍으로 시도해 보았습니다.

다음은 간단한 예입니다. 자세한 내용은 링크를 참조하십시오.

testSort(data) {
    return data.sort(
        a['nameOne'] > b['nameOne'] ? 1
        : b['nameOne'] > a['nameOne'] ? -1 : 0 ||
        a['date'] > b['date'] ||
        a['number'] - b['number']
    );
}

0

다음은 참고 용 광산입니다.

function msort(arr, ...compFns) {
  let fn = compFns[0];
  arr = [].concat(arr);
  let arr1 = [];
  while (arr.length > 0) {
    let arr2 = arr.splice(0, 1);
    for (let i = arr.length; i > 0;) {
      if (fn(arr2[0], arr[--i]) === 0) {
        arr2 = arr2.concat(arr.splice(i, 1));
      }
    }
    arr1.push(arr2);
  }

  arr1.sort(function (a, b) {
    return fn(a[0], b[0]);
  });

  compFns = compFns.slice(1);
  let res = [];
  arr1.map(a1 => {
    if (compFns.length > 0) a1 = msort(a1, ...compFns);
    a1.map(a2 => res.push(a2));
  });
  return res;
}

let tstArr = [{ id: 1, sex: 'o' }, { id: 2, sex: 'm' }, { id: 3, sex: 'm' }, { id: 4, sex: 'f' }, { id: 5, sex: 'm' }, { id: 6, sex: 'o' }, { id: 7, sex: 'f' }];

function tstFn1(a, b) {
  if (a.sex > b.sex) return 1;
  else if (a.sex < b.sex) return -1;
  return 0;
}

function tstFn2(a, b) {
  if (a.id > b.id) return -1;
  else if (a.id < b.id) return 1;
  return 0;
}

console.log(JSON.stringify(msort(tstArr, tstFn1, tstFn2)));
//output:
//[{"id":7,"sex":"f"},{"id":4,"sex":"f"},{"id":5,"sex":"m"},{"id":3,"sex":"m"},{"id":2,"sex":"m"},{"id":6,"sex":"o"},{"id":1,"sex":"o"}]

0

나는 비슷한 것을 찾고 있었고 이것으로 끝났다.

먼저 하나 이상의 정렬 함수가 있으며 항상 0, 1 또는 -1을 반환합니다.

const sortByTitle = (a, b): number => 
  a.title === b.title ? 0 : a.title > b.title ? 1 : -1;

정렬하려는 서로 다른 속성에 대해 더 많은 기능을 만들 수 있습니다.

그런 다음 이러한 정렬 기능을 하나로 결합하는 기능이 있습니다.

const createSorter = (...sorters) => (a, b) =>
  sorters.reduce(
    (d, fn) => (d === 0 ? fn(a, b) : d),
    0
  );

위의 정렬 함수를 읽을 수있는 방식으로 결합하는 데 사용할 수 있습니다.

const sorter = createSorter(sortByTitle, sortByYear)

items.sort(sorter)

정렬 함수가 0을 반환하면 다음 정렬 함수가 추가 정렬을 위해 호출됩니다.


0

또 다른 옵션입니다. 다음 유틸리티 기능을 사용하십시오.

/** Performs comparing of two items by specified properties
 * @param  {Array} props for sorting ['name'], ['value', 'city'], ['-date']
 * to set descending order on object property just add '-' at the begining of property
 */
export const compareBy = (...props) => (a, b) => {
  for (let i = 0; i < props.length; i++) {
    const ascValue = props[i].startsWith('-') ? -1 : 1;
    const prop = props[i].startsWith('-') ? props[i].substr(1) : props[i];
    if (a[prop] !== b[prop]) {
      return a[prop] > b[prop] ? ascValue : -ascValue;
    }
  }
  return 0;
};

사용 예 (귀하의 경우) :

homes.sort(compareBy('city', '-price'));

'address.city'또는 'style.size.width'등과 같은 중첩 된 속성을 사용할 수 있도록이 기능을 더욱 일반화 할 수 있습니다.


0

비교하기 전에 값을 형식화 할 수있는 기회를 가지면서 여러 필드로 정렬하는 재귀 알고리즘입니다.

var data = [
{
    "id": 1,
    "ship": null,
    "product": "Orange",
    "quantity": 7,
    "price": 92.08,
    "discount": 0
},
{
    "id": 2,
    "ship": "2017-06-14T23:00:00.000Z".toDate(),
    "product": "Apple",
    "quantity": 22,
    "price": 184.16,
    "discount": 0
},
...
]
var sorts = ["product", "quantity", "ship"]

// comp_val formats values and protects against comparing nulls/undefines
// type() just returns the variable constructor
// String.lower just converts the string to lowercase.
// String.toDate custom fn to convert strings to Date
function comp_val(value){
    if (value==null || value==undefined) return null
    var cls = type(value)
    switch (cls){
        case String:
            return value.lower()
    }
    return value
}

function compare(a, b, i){
    i = i || 0
    var prop = sorts[i]
    var va = comp_val(a[prop])
    var vb = comp_val(b[prop])

    // handle what to do when both or any values are null
    if (va == null || vb == null) return true

    if ((i < sorts.length-1) && (va == vb)) {
        return compare(a, b, i+1)
    } 
    return va > vb
}

var d = data.sort(compare);
console.log(d);

a와 b가 같으면 사용할 수 없을 때까지 다음 필드를 시도합니다.


-1
homes.sort(function(a,b) { return a.city - b.city } );
homes.sort(function(a,b){
    if (a.city==b.city){
        return parseFloat(b.price) - parseFloat(a.price);
    } else {
        return 0;
    }
});

왜 모든 것을 단일 함수에 넣지 않습니까? 도시가 같지 않다면, 그들의 diff를 반환하고, 그렇지 않으면 가격을 diff하십시오.
Mad Physicist

-1

여기서 'AffiliateDueDate'와 'Title'은 열이며 둘 다 오름차순으로 정렬됩니다.

array.sort(function(a, b) {

               if (a.AffiliateDueDate > b.AffiliateDueDate ) return 1;
               else if (a.AffiliateDueDate < b.AffiliateDueDate ) return -1;
               else if (a.Title > b.Title ) return 1;
               else if (a.Title < b.Title ) return -1;
               else return 0;
             })

-1

두 개의 날짜 필드와 숫자 필드 예를 기준으로 정렬 :

var generic_date =  new Date(2070, 1, 1);
checkDate = function(date) {
  return Date.parse(date) ? new Date(date): generic_date;
}

function sortData() {  
  data.sort(function(a,b){
    var deltaEnd = checkDate(b.end) - checkDate(a.end);
    if(deltaEnd) return deltaEnd;

    var deltaRank = a.rank - b.rank;
    if (deltaRank) return deltaRank;

    var deltaStart = checkDate(b.start) - checkDate(a.start);
    if(deltaStart) return deltaStart;

    return 0;
  });
}

http://jsfiddle.net/hcWgf/57/


-1
function sort(data, orderBy) {
        orderBy = Array.isArray(orderBy) ? orderBy : [orderBy];
        return data.sort((a, b) => {
            for (let i = 0, size = orderBy.length; i < size; i++) {
                const key = Object.keys(orderBy[i])[0],
                    o = orderBy[i][key],
                    valueA = a[key],
                    valueB = b[key];
                if (!(valueA || valueB)) {
                    console.error("the objects from the data passed does not have the key '" + key + "' passed on sort!");
                    return [];
                }
                if (+valueA === +valueA) {
                    return o.toLowerCase() === 'desc' ? valueB - valueA : valueA - valueB;
                } else {
                    if (valueA.localeCompare(valueB) > 0) {
                        return o.toLowerCase() === 'desc' ? -1 : 1;
                    } else if (valueA.localeCompare(valueB) < 0) {
                        return o.toLowerCase() === 'desc' ? 1 : -1;
                    }
                }
            }
        });
    }

사용 :

sort(homes, [{city : 'asc'}, {price: 'desc'}])


-1

이 간단한 솔루션은 어떻습니까?

const sortCompareByCityPrice = (a, b) => {
    let comparison = 0
    // sort by first criteria
    if (a.city > b.city) {
        comparison = 1
    }
    else if (a.city < b.city) {
        comparison = -1
    }
    // If still 0 then sort by second criteria descending
    if (comparison === 0) {
        if (parseInt(a.price) > parseInt(b.price)) {
            comparison = -1
        }
        else if (parseInt(a.price) < parseInt(b.price)) {
            comparison = 1
        }
    }
    return comparison 
}

이 질문 에 따라 여러 (숫자) 필드로 자바 스크립트 정렬 배열

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.