자바 스크립트 : 자연 숫자의 영숫자 문자열


173

숫자와 텍스트로 구성된 배열을 정렬하는 가장 쉬운 방법을 찾고 있습니다.

예 :

'123asd'
'19asd'
'12345asd'
'asd123'
'asd12'

로 변하다

'19asd'
'123asd'
'12345asd'
'asd12'
'asd123'

이것은 내가 여기서 물었던 다른 질문에 대한 해결책과 함께 사용될 것 입니다.

정렬 기능 자체가 작동합니다. 필요한 것은 '19asd'가 '123asd'보다 작다는 말할 수있는 기능입니다.

나는 이것을 JavaScript로 작성하고 있습니다.

편집 : adormitu가 지적했듯이, 내가 찾고있는 것은 자연 정렬 기능입니다.


또한 볼 How do you do string comparison in JavaScript?에 stackoverflow.com/questions/51165/...
— 아드 리앙은 수

1
원래 질문은 2010 년에 요청되었으므로 놀라운 것은 아닙니다. :)
— ptrn

답변:


316

이제 localeCompare를 사용하는 최신 브라우저에서 가능합니다. numeric: true옵션 을 전달 하면 스마트하게 숫자를 인식합니다. 을 사용하여 대소 문자를 구분할 수 있습니다 sensitivity: 'base'. Chrome, Firefox 및 IE11에서 테스트되었습니다.

다음은 예입니다. 그것은 반환 1(10)가 2 후에가는 의미 :

'10'.localeCompare('2', undefined, {numeric: true, sensitivity: 'base'})

많은 수의 문자열을 정렬 할 때의 성능을 위해이 기사에서는 다음과 같이 말합니다.

큰 배열 정렬과 같이 많은 수의 문자열을 비교할 때는 Intl.Collator 객체를 만들고 compare 속성에서 제공하는 함수를 사용하는 것이 좋습니다. 문서 링크

var collator = new Intl.Collator(undefined, {numeric: true, sensitivity: 'base'});
var myArray = ['1_Document', '11_Document', '2_Document'];
console.log(myArray.sort(collator.compare));


12
: 당신이 객체의 배열을 정렬 할 경우, 당신은 또한 조합기 사용할 수 있습니다 codepen.io/TimPietrusky/pen/rKzoGN
— TimPietrusky

2
위의 설명을 명확하게하기 위해 : "locales 인수가 제공되지 않거나 정의되지 않은 경우 런타임의 기본 로케일이 사용됩니다."
— gkiely

46

그래서 당신은 자연적인 정렬 이 필요 합니까?

그렇다면 David koelle의 작업을 기반으로 한 Brian Huisman의이 대본 보다 필요한 것입니다.

Brian Huisman의 솔루션이 David Koelle의 블로그에서 직접 호스팅되는 것 같습니다.


내가 찾는 것은 정확하고 자연스러운 종류입니다. 감사합니다
— ptrn

그것은 매우 부 자연스러운 종류입니다. Alphbetic 종류를 생성하지 않습니다.
— tchrist

@tchrist : "알파벳 정렬을하지 않습니다"는 무슨 뜻입니까?
— Adrien Be

제대로 작동하지만 음수를 올바르게 처리하지 못합니다. 즉, [ '-1'을 생산할 것입니다. '-2', '0', '1', '2'].
— adrianboimvaser

2
@mhitza이 코드는 좋은 작업을 할 것 github.com/litejs/natural-compare-lite 빠른 테스트 참조 jsbin.com/bevututodavi/1/edit?js,console
— 아드는 수

23

값을 비교하기 위해 비교 방법을 사용할 수 있습니다.

function naturalSorter(as, bs){
    var a, b, a1, b1, i= 0, n, L,
    rx=/(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.\D+)|(\.$)/g;
    if(as=== bs) return 0;
    a= as.toLowerCase().match(rx);
    b= bs.toLowerCase().match(rx);
    L= a.length;
    while(i<L){
        if(!b[i]) return 1;
        a1= a[i],
        b1= b[i++];
        if(a1!== b1){
            n= a1-b1;
            if(!isNaN(n)) return n;
            return a1>b1? 1:-1;
        }
    }
    return b[i]? -1:0;
}

그러나 배열 정렬 속도를 높이려면 정렬하기 전에 배열을 조작하십시오. 따라서 정렬 단계마다 소문자 변환과 정규 표현식을 한 번만 수행하면됩니다.

function naturalSort(ar, index){
    var L= ar.length, i, who, next, 
    isi= typeof index== 'number', 
    rx=  /(\.\d+)|(\d+(\.\d+)?)|([^\d.]+)|(\.(\D+|$))/g;
    function nSort(aa, bb){
        var a= aa[0], b= bb[0], a1, b1, i= 0, n, L= a.length;
        while(i<L){
            if(!b[i]) return 1;
            a1= a[i];
            b1= b[i++];
            if(a1!== b1){
                n= a1-b1;
                if(!isNaN(n)) return n;
                return a1>b1? 1: -1;
            }
        }
        return b[i]!= undefined? -1: 0;
    }
    for(i= 0; i<L; i++){
        who= ar[i];
        next= isi? ar[i][index] || '': who;
        ar[i]= [String(next).toLowerCase().match(rx), who];
    }
    ar.sort(nSort);
    for(i= 0; i<L; i++){
        ar[i]= ar[i][1];
    }
}

내부 배열이 외부 배열의 순서를 결정 하면서이 경우 작동합니까?
— ptrn

무엇입니까 String.prototype.tlc()? 이 코드는 자신의 코드입니까, 아니면 어딘가에서 얻었습니까? 후자의 경우 페이지에 링크하십시오.
— Andy E

실수 수정에 대해 죄송합니다. 감사합니다. a [1] 및 b [1]이 정렬을 제어하도록하려면 a = String (a [1]). toLowerCase (); b = 문자열 (b [1]). toLowerCase ();
— kennebec

방금 정렬하려는 데이터 목록이 있었고 Chrome Dev Tools 콘솔에서 쉽게 수행 할 수 있다고 생각했습니다. 기능 덕분에!
— ajh158

9

객체 배열이 있으면 다음과 같이 할 수 있습니다.

myArrayObjects = myArrayObjects.sort(function(a, b) {
  return a.name.localeCompare(b.name, undefined, {
    numeric: true,
    sensitivity: 'base'
  });
});


1
완벽한 답변! 감사합니다.
— hubert17

5

2019 년 현재이 기능을 처리 할 수있는 가장 완벽한 기능을 갖춘 라이브러리는 당연한 것 같습니다 .

const { orderBy } = require('natural-orderby')

const unordered = [
  '123asd',
  '19asd',
  '12345asd',
  'asd123',
  'asd12'
]

const ordered = orderBy(unordered)

// [ '19asd',
//   '123asd',
//   '12345asd',
//   'asd12',
//   'asd123' ]

문자열 배열뿐만 아니라 객체 배열의 특정 키 값을 기준으로 정렬 할 수도 있습니다. 또한 통화, 날짜, 통화 및 기타 여러 가지 문자열을 자동으로 식별하고 정렬 할 수 있습니다.

놀랍게도 gzipped했을 때 1.6kB에 불과합니다.


2

다음을 변환하는 8 자리 패딩 함수를 상상해보십시오.

  • '123asd'-> '00000123asd'
  • '19asd'-> '00000019asd'

채워진 문자열을 사용하여 '123asd'앞에 '19asd'가 표시되도록 정렬 할 수 있습니다.

정규식 /\d+/g을 사용하여 채워야하는 모든 숫자를 찾는 데 도움이됩니다.

str.replace(/\d+/g, pad)

다음은이 기술을 사용한 정렬을 보여줍니다.

var list = [
    '123asd',
    '19asd',
    '12345asd',
    'asd123',
    'asd12'
];

function pad(n) { return ("00000000" + n).substr(-8); }
function natural_expand(a) { return a.replace(/\d+/g, pad) };
function natural_compare(a, b) {
    return natural_expand(a).localeCompare(natural_expand(b));
}

console.log(list.map(natural_expand).sort()); // intermediate values
console.log(list.sort(natural_compare)); // result

중간 결과는 natural_expand () 루틴의 기능을 보여주고 후속 natural_compare 루틴의 작동 방식을 이해합니다.

[
  "00000019asd",
  "00000123asd",
  "00012345asd",
  "asd00000012",
  "asd00000123"
]

출력 :

[
  "19asd",
  "123asd",
  "12345asd",
  "asd12",
  "asd123"
]

1

위의 @Adrien Be의 대답을 바탕으로 Brian Huisman & David koelle이 만든 코드를 사용하여 객체 배열에 대한 수정 된 프로토 타입 정렬이 있습니다.

//Usage: unsortedArrayOfObjects.alphaNumObjectSort("name");
//Test Case: var unsortedArrayOfObjects = [{name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a10"}, {name: "a5"}, {name: "a13"}, {name: "a20"}, {name: "a8"}, {name: "8b7uaf5q11"}];
//Sorted: [{name: "8b7uaf5q11"}, {name: "a1"}, {name: "a2"}, {name: "a3"}, {name: "a5"}, {name: "a8"}, {name: "a10"}, {name: "a13"}, {name: "a20"}]

// **Sorts in place**
Array.prototype.alphaNumObjectSort = function(attribute, caseInsensitive) {
  for (var z = 0, t; t = this[z]; z++) {
    this[z].sortArray = new Array();
    var x = 0, y = -1, n = 0, i, j;

    while (i = (j = t[attribute].charAt(x++)).charCodeAt(0)) {
      var m = (i == 46 || (i >=48 && i <= 57));
      if (m !== n) {
        this[z].sortArray[++y] = "";
        n = m;
      }
      this[z].sortArray[y] += j;
    }
  }

  this.sort(function(a, b) {
    for (var x = 0, aa, bb; (aa = a.sortArray[x]) && (bb = b.sortArray[x]); x++) {
      if (caseInsensitive) {
        aa = aa.toLowerCase();
        bb = bb.toLowerCase();
      }
      if (aa !== bb) {
        var c = Number(aa), d = Number(bb);
        if (c == aa && d == bb) {
          return c - d;
        } else {
          return (aa > bb) ? 1 : -1;
        }
      }
    }

    return a.sortArray.length - b.sortArray.length;
  });

  for (var z = 0; z < this.length; z++) {
    // Here we're deleting the unused "sortArray" instead of joining the string parts
    delete this[z]["sortArray"];
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.