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


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.