본문 바로가기
Algorithm/ProblemSolving

[CodingTest]프로그래머스 파일명 정렬

by sjwdev 2020. 12. 28.

# 시작 

알고리즘 스터디 12월 넷째주 과제 2번째 문제다. 정렬 문제라서 여행 경로보다는 익숙한 것 같다. img1.png와 img12.png, img2.png가 있을 때 img1.png, img2.png, img.12.png 순으로 정렬되는 것이 순서상으로 자연스럽다는 것인데, 이를 위해서 파일명에 포함된 숫자를 반영한 정렬 기능을 구현해야 한다. 


# 문제 

세 차례의 코딩 테스트와 두 차례의 면접이라는 기나긴 블라인드 공채를 무사히 통과해 카카오에 입사한 무지는 파일 저장소 서버 관리를 맡게 되었다.

저장소 서버에는 프로그램의 과거 버전을 모두 담고 있어, 이름 순으로 정렬된 파일 목록은 보기가 불편했다. 파일을 이름 순으로 정렬하면 나중에 만들어진 ver-10.zip이 ver-9.zip보다 먼저 표시되기 때문이다.

버전 번호 외에도 숫자가 포함된 파일 목록은 여러 면에서 관리하기 불편했다. 예컨대 파일 목록이 [img12.png, img10.png, img2.png, img1.png]일 경우, 일반적인 정렬은 [img1.png, img10.png, img12.png, img2.png] 순이 되지만, 숫자 순으로 정렬된 [img1.png, img2.png, img10.png, img12.png"] 순이 훨씬 자연스럽다.

무지는 단순한 문자 코드 순이 아닌, 파일명에 포함된 숫자를 반영한 정렬 기능을 저장소 관리 프로그램에 구현하기로 했다.

소스 파일 저장소에 저장된 파일명은 100 글자 이내로, 영문 대소문자, 숫자, 공백(" "), 마침표("."), 빼기 부호("-")만으로 이루어져 있다. 파일명은 영문자로 시작하며, 숫자를 하나 이상 포함하고 있다.

파일명은 크게 HEAD, NUMBER, TAIL의 세 부분으로 구성된다.

  • HEAD는 숫자가 아닌 문자로 이루어져 있으며, 최소한 한 글자 이상이다.
  • NUMBER는 한 글자에서 최대 다섯 글자 사이의 연속된 숫자로 이루어져 있으며, 앞쪽에 0이 올 수 있다. 0부터 99999 사이의 숫자로, 00000이나 0101 등도 가능하다.
  • TAIL은 그 나머지 부분으로, 여기에는 숫자가 다시 나타날 수도 있으며, 아무 글자도 없을 수 있다.

파일명 예시

파일명을 세 부분으로 나눈 후, 다음 기준에 따라 파일명을 정렬한다.

  • 파일명은 우선 HEAD 부분을 기준으로 사전 순으로 정렬한다. 이때, 문자열 비교 시 대소문자 구분을 하지 않는다. MUZI와 muzi, MuZi는 정렬 시에 같은 순서로 취급된다.
  • 파일명의 HEAD 부분이 대소문자 차이 외에는 같을 경우, NUMBER의 숫자 순으로 정렬한다. 9 < 10 < 0011 < 012 < 13 < 014 순으로 정렬된다. 숫자 앞의 0은 무시되며, 012와 12는 정렬 시에 같은 같은 값으로 처리된다.
  • 두 파일의 HEAD 부분과, NUMBER의 숫자도 같을 경우, 원래 입력에 주어진 순서를 유지한다. MUZI01.zip과 muzi1.png가 입력으로 들어오면, 정렬 후에도 입력 시 주어진 두 파일의 순서가 바뀌어서는 안 된다.

무지를 도와 파일명 정렬 프로그램을 구현하라.

 

# 입력 형식

입력으로 배열 files가 주어진다.

  • files는 1000 개 이하의 파일명을 포함하는 문자열 배열이다.
  • 각 파일명은 100 글자 이하 길이로, 영문 대소문자, 숫자, 공백(" ), 마침표(.), 빼기 부호(-")만으로 이루어져 있다. 파일명은 영문자로 시작하며, 숫자를 하나 이상 포함하고 있다.
  • 중복된 파일명은 없으나, 대소문자나 숫자 앞부분의 0 차이가 있는 경우는 함께 주어질 수 있다. (muzi1.txt, MUZI1.txt, muzi001.txt, muzi1.TXT는 함께 입력으로 주어질 수 있다.)

# 출력 형식

위 기준에 따라 정렬된 배열을 출력한다.

 

# 입출력 예제


# 과정

1. HEAD, NUMBER, TAIL의 세 부분으로 나누고 어떤 배열(인덱스는 0,1,2)에 넣어야 될 것 같다.

  • HEAD는 숫자가 아닌 문자로 이루어져 있으며, 최소한 한 글자 이상이다.
  • NUMBER는 한 글자에서 최대 다섯 글자 사이의 연속된 숫자로 이루어져 있으며, 앞쪽에 0이 올 수 있다. 0부터 99999 사이의 숫자로, 00000이나 0101 등도 가능하다.
  • TAIL은 그 나머지 부분으로, 여기에는 숫자가 다시 나타날 수도 있으며, 아무 글자도 없을 수 있다.

파일명 예시

- 자바스크립트에서 배열의 길이를 체크하려면 배열.length를 사용한다.(배열.length()를 사용함)

  for (let i = 0; i < files.length; i++) {

 

- 자바스크립트에서 문자열을 아스키 코드로 변환하는 것은 chatAt()이 아니라 문자열.charCodeAt([문자열자릿수]) 사용

      tempCh = files[i].charCodeAt(j);

 

- 문자열을 대소문자 구분없이 문자열인지 확인하기 위해서 전부 대문자화하는 데 문자열.toUpperCase()를 사용, 아스키

코드상 48보다 작거나 57보다 큰 값은 문자로 취급할 때 대문자화가 필요하다.

    let input = files[i].toUpperCase();
    
    // ,,,
    
    if (tempCh < 48 || tempCh > 57) {

 

- input(문자열)를 한 문자씩 인덱스를 접근하기 위해서는 문자열.charAt(j)로 접근

        str.concat(input.charAt(j));

 

- [[ head, number, tail], [head, number, tail],,, 반복 ] 과 같은 이차원 배열을 만들어야할 것 같다. 그런데 자바스크립트는 이차원 배열 선언이 안된다. 아래와 같은 방식(es6 지원시)으로 가능하다.

// arr[5][2] (빈 배열 생성)
const arr1 = Array.from(Array(5), () => new Array(2)

하지만 이차원 배열의 배열 객체들이 파일명의 개수에 따라 몇개가 될지 알 수 없어서 이차원 배열을 선언해서 쓰는 게 아니라 중간에 tmp 배열에 str(문자열)을 담아준다음 배열 객체의 첫번째 인덱스에 넣는 식으로 했다.

const files = ['img12.png', 'img10.png', 'img02.png', 'img1.png', 'IMG01.GIF', 'img2.JPG'];

입력으로 주어진 files가 위와 같다면 문자열 부분은 각 배열 객체 내의 head에 위치할 것이다. 아래와 같이 출력됨을 확인했다.

 

- for 문 내에서 head, number, tail을 분리하면서 tail에 영어 대소문자, 특수문자, 숫자 뭐든 들어갈 수 있다는 점 때문에 if문의 조건을 어떻게 줘야할지 문제였다. 그래서 number를 인식한 순간 chk라는 플래그를 true로 바꿔서 그다음에 숫자 인식이 끝나면 tail로 인식하게끔 하였다.

그런데 문제가 나타났던 것은 확장자 앞에 있는 "."이 head부분에 인식된다는 점이였다. 

if (tempCh < 48 || tempCh > 57 && chk === false) {
        str += files[i].charAt(j);
        console.log('str', str);
      } else if (tempCh >= 48 && tempCh <= 57) {
        /* tempCh 가 숫자인 경우 */
        num += files[i].charAt(j);
        console.log('num', num);
        chk = true;
      } else {
        rem += files[i].charAt(j);
        console.log('rem', rem);
      }

처음 if문을 보면 tempCh < 48 || tempCh > 57 && chk === false라고 되어있다. 그런데 저장을 하고 실행시키면 뒤의 tempCh > 57 && chk === false 부분이 괄호로 바뀌면서 조건이 잘못되는 것이였다.

이를 해결하기 위해서 아래와 같이 조건문을 변경했다.

      /* tempCh 가 영어 대소문자인 경우 */
      if ((tempCh < 48 || tempCh > 57) && chk === false) {
        str += files[i].charAt(j);
        console.log('str', str);
      } else if (tempCh >= 48 && tempCh <= 57) {
        /* tempCh 가 숫자인 경우 */
        num += files[i].charAt(j);
        console.log('num', num);
        chk = true;
      } else {
        rem += files[i].charAt(j);
        console.log('rem', rem);
      }

tempCh로 영어 대소문자가 맞는지 먼저 검사하게 하고 이것을 t,f로 판단한다음 그 값으로 뒤의 chk 값이 false면 실행하도록 하였다. 위에서 발생했던 문제는 일단 tempCh < 48 그러니까 특수문자여도 되고, 그게 아니라면 (tempCh > 57 && chk === false)면 수행한다고 조건을 줬기 때문에 str이 "img."와 같이 뒤의 특수문자가 나타났을 때에도 그대로 이 if문을 수행했던 것이였다.

결과적으로 이러한 배열을 얻을 수 있었다.

 


2. 문자열 정렬 구현 순서

2.1 문자열 비교 시 대소문자 구분을 하지 않는다.MUZI와 muzi, MuZi는 정렬 시에 같은 순서로 취급

if (o1[i].toLowerCase().localeCompare(o2[i]) > 0) {

2.2 파일명의 HEAD 부분이 대소문자 차이 외에는 같을 경우, NUMBER의 숫자 순으로 정렬한다. 9 < 10 < 0011 < 012 < 13 < 014 순으로 정렬된다. 숫자 앞의 0은 무시되며, 012와 12는 정렬 시에 같은 같은 값으로 처리된다.

var a = '1000';
console.log(parseInt(a));
var b = '02000';
console.log(parseInt(b));

if (parseInt(a) < parseInt(b)) {
  console.log('a는 b보다 작다');
} else {
  console.log('a는 b보다 크다');
}

NUMBER부분의 숫자는 문자열 형식이므로 위와 같이 문자열을 정수형으로 변환하는 parseInt(x)를 사용하면 sort에 이용할 수 있다. (숫자 앞에 붙은 0도 무시할 수 있다.)

 


2.3 두 파일의 HEAD 부분과, NUMBER의 숫자도 같을 경우, 원래 입력에 주어진 순서를 유지한다. MUZI01.zip과 muzi1.png가 입력으로 들어오면, 정렬 후에도 입력 시 주어진 두 파일의 순서가 바뀌어서는 안 된다.

 

아마도 이 부분을 제대로 구현 못한 것 같다.

아래와 같이 구현했는데 테스트케이스 2개중 1개만 통과되었다.

// 테스트 케이스
const files = ['img12.png', 'img10.png', 'img02.png', 'img1.png', 'IMG01.GIF', 'img2.JPG'];
// const files = ['F-5 Freedom Fighter', 'B-50 Superfortress', 'A-10 Thunderbolt II', 'F-14 Tomcat'];

function solution(files) {
  let answer = check(files);
  let tmpanswer = answer;
  let chk = false;

  tmpanswer.sort(function (o1, o2) {
    for (var i = 0; i < o1.length; i++) {
      if (!(o1[i] >= 48 && o1[i] <= 57) && chk === false) {
        if (o1[i].toLowerCase().localeCompare(o2[i]) > 0) {
          return -1; // 바꾼다.
        } else if (o1[i].toLowerCase().localeCompare(o2[i]) < 0) {
          return 1; // 원래 앞에 있던 걸(o2) 그대로 둔다.
        } else {
          if (parseInt(o1[1]) === parseInt(o2[1])) {
          }
          if (parseInt(o1[1]) < parseInt(o2[1])) {
            return -1; // 바꾼다.
          } else {
            return 1;
          }
        }
      }
    }
    return 0;
  });

  answer.map((x, i) => {
    var str = '';
    x.map((item, i) => {
      str += item;
    });
    x[i] = str;
    str = '';
    // console.log(x[i]);
    answer[i] = x[i];
  });

  return answer;
}

function check(files) {
  let str = '';
  let num = '';
  let rem = '';
  let tmp = [];
  let result = [];
  let chk = false;

  for (let i = 0; i < files.length; i++) {
    let input = files[i].toUpperCase();
    for (let j = 0; j < input.length; j++) {
      let tempCh = input.charCodeAt(j);
      if ((tempCh < 48 || tempCh > 57) && chk === false) {
        str += files[i].charAt(j);
        // console.log('str', str);
      } else if (tempCh >= 48 && tempCh <= 57) {
        num += files[i].charAt(j);
        // console.log('num', num);
        chk = true;
      } else {
        rem += files[i].charAt(j);
        // console.log('rem', rem);
      }
    }
    tmp = [];
    tmp.push(str);
    tmp.push(num);
    tmp.push(rem);
    result[i] = tmp;
    str = '';
    num = '';
    rem = '';
    chk = false;
  }
  return result;
}

실행한 결괏값을 보면 A-10~이여야 되는데 F-5인 것부터 이미 많이 엇나가있음을 알 수 있었다..

 

 

이번에는 테스트케이스 2에 맞춰 조건을 수정했는데 테스트케이스 1이 실패했다.

// 테스트 케이스
// const files = ['img12.png', 'img10.png', 'img02.png', 'img1.png', 'IMG01.GIF', 'img2.JPG'];
const files = ['F-5 Freedom Fighter', 'B-50 Superfortress', 'A-10 Thunderbolt II', 'F-14 Tomcat'];

function solution(files) {
  let answer = check(files);
  let tmpanswer = answer;
  let chk = false;
  console.log(files);

  // 배열 내림차순 정렬
  tmpanswer.sort(function (o1, o2) {
    for (var i = 0; i < o1.length; i++) {
      console.log('o1[i]', o1[i]);
      // NOTE: 해당 문자열이 영어 대소문자인 경우 일반적인 사전식 비교 정렬
      // console.log('o1', o1[i]);
      // console.log('o2', o2[i]);
      if (!(o1[i] >= 48 && o1[i] <= 57) && chk === false) {
        console.log('숫자가 아니다.', o1[i].toLowerCase(), o2[i].toLowerCase());
        if (
          !(o1[i].toLowerCase() === o2[i].toLowerCase()) &&
          o1[i].toLowerCase().localeCompare(o2[i]) < 0
        ) {
          console.log(o2[i].toLowerCase(), '와 바꾸자');
          console.log();
          return -1; // 바꾼다.
        } else if (o1[i].toLowerCase().localeCompare(o2[i]) > 0) {
          return 1; // 원래 앞에 있던 걸(o2) 그대로 둔다.
        } else {
          console.log('숫자임');
          //   console.log('o1', o1[1]);
          //   console.log('o2', o2[1]);
          if (parseInt(o1[1]) === parseInt(o2[1])) {
            console.log('같다');
            if (o1[1].length < o2[1].length) {
              return -1;
            } else {
              return 1;
            }
          }
          if (parseInt(o1[1]) < parseInt(o2[1])) {
            // if (o1[1].length > o2[1].length) {
            console.log(o1[1], '는 ', o2[1], '보다 작다');
            return -1; // 바꾼다.
            // } else {
            //   return 1;
            // }
          } else {
            console.log(o1[1], '는 ', o2[1], '보다 크다');
            return 1;
          }
        }
      }
    }
    return 0;
  });

  // NOTE: HEAD, NUMBER, TAIL 연결
  answer.map((x, i) => {
    var str = '';
    console.log(x);
    x.map((item, i) => {
      console.log(item);
      str += item;
    });
    x[i] = str;
    str = '';
    // console.log(x[i]);
    answer[i] = x[i];
  });

  console.log('Hanswer', answer);

  return answer;
}

// NOTE: 1. HEAD, NUMBER, TAIL 분리
function check(files) {
  let str = '';
  let num = '';
  let rem = '';
  let tmp = [];
  let result = [];
  let chk = false;

  for (let i = 0; i < files.length; i++) {
    let input = files[i].toUpperCase();
    for (let j = 0; j < input.length; j++) {
      let tempCh = input.charCodeAt(j);
      /* HEAD : tempCh가 영어 대소문자인 경우 */
      if ((tempCh < 48 || tempCh > 57) && chk === false) {
        str += files[i].charAt(j);
        // console.log('str', str);
      } else if (tempCh >= 48 && tempCh <= 57) {
        /* NUMBNER : tempCh가 숫자인 경우 */
        num += files[i].charAt(j);
        // console.log('num', num);
        chk = true;
      } else {
        /* TAIL */
        rem += files[i].charAt(j);
        // console.log('rem', rem);
      }
    }
    tmp = [];
    tmp.push(str);
    tmp.push(num);
    tmp.push(rem);
    result[i] = tmp;
    str = '';
    num = '';
    rem = '';
    chk = false;
  }
  return result;
}

solution(files);

 

그래서 조건을 돌아보니 파일명이 파싱 결과 서로 같을 때 순서를 정하는 것은 입력 받은 순서를 그대로 지키는 것이였고 중간에 조건문을 삭제시키니 테스트케이스 2개가 전부 통과되었다. 하지만 제출해보면 틀린 게 많았다.

구조적으로는 맞는데 내부적인 조건을 선정하는 것이 너무 복잡했다. 그래서 구글링을 통해 sort 함수를 다루는 예시를 좀더 공부했다. 컴파일러 과목에서 배운 정규표현식도 떠올랐었는데 실제로 이를 사용하는 것이 효과적인 것 같다. 


function solution(files) {
  // var answer = [];
  let answerWrap = files.map((file, index) => ({ file, index }));

  // 파일명 구분 H, N, T
  // H - 사전순. 대소문자 구별X
  // N - 맨앞 0 무시
  // T 고려하지 않고 원래 순서대로 정렬 - 어차피 T무시니까 소문자 통일 가능

  // for(var i=0; i<files.length; i++){
  //    files[i] = files[i].toLowerCase();
  // } //비교할 때만 바꿔야 해.

  var compare = (a, b) => {
    var regexNum = /[0-9]/g;

    // regex에 매치되는 첫 Index 찾기
    var numIndexA = a.indexOf(a.match(regexNum)[0]);
    var numIndexB = b.indexOf(b.match(regexNum)[0]);

    // Head 기준 정렬
    var sortByHead = a
      .substring(0, numIndexA)
      .toLowerCase()
      .localeCompare(b.substring(0, numIndexB).toLowerCase());
    //       console.log((a.substring(0, numIndexA)).toLowerCase());

    //1, -1, 0
    if (sortByHead === 0) {
      // Num기준 정렬
      var subStrA = parseInt(a.substring(numIndexA));
      var subStrB = parseInt(b.substring(numIndexB));
      if (subStrA < subStrB) {
        return -1;
      } else if (subStrA > subStrB) {
        return 1;
      } else {
        return 0;
      }
    } else if (sortByHead === -1) {
      return -1;
    } else {
      return 1;
    }
  };

  answerWrap.sort((a, b) => {
    var result = compare(a.file, b.file);
    return result === 0 ? a.index - b.index : result;
  });
  return answerWrap.map(answer => answer.file);
}

- sort에서 구현이 미흡했는데 처음에 files를 받아서 각 배열 객체를 file, index라는 키를 주고 만드는 이 부분이 관건인 것같다. 왜냐하면 sort 함수가 불안정하여 a, b가 같을 때 순서를 그대로 맞춰주기 위해서는 원래의 순서를 기억하고 있어야 하기 때문이다. 그밖에도 많은 함수들을 활용하지 못하고 있어서 이것들을 파악하는 데에 중점을 두었다.

  let answerWrap = files.map((file, index) => ({ file, index }));

 

- 정규식은 아래와 같이 숫자에 대응하고 해당 숫자를 만나면 그것을 가리키게 된다.

    var regexNum = /[0-9]/g;

g: 문자열 전체를 확인한다.

참고로 ^[0-9]가 자꾸 나와서 확인해보니 아래와 같았다.

문자열 문제가 많다보니 정규식을 이용할 일이 많을 것 같은데 시간이 나면 따로 글로 정리해야겠다. 

 

- match()

해당 문자열.match('찾을 단어')
// match()함수는 인자에 포함된 문자를 찾으면 이를 반환함

 // regex에 매치되는 첫 Index 찾기
    var numIndexA = a.indexOf(a.match(regexNum)[0]);
    var numIndexB = b.indexOf(b.match(regexNum)[0]);

위에서 선언한 정규식인 regexNum을 사용해 match에 넣어 사용하니 코드가 굉장히 깔끔해졌다. match()는 특정 텍스트 안에 검색할 단어가 있는 경우 해당 텍스트가 포함되어 있는지 확인할 수 있다. 정규표현식으로 특정 패턴을 검색하는 것도 가능하다. 

a.match(regexNum)[0]이 되면 결과적으로 숫자에 해당하는 패턴이 a라는 문자열에 존재하는지 찾아보고 이 문자를 찾으면 반환을 해준다. 아래와 같이 작동했다.

실제로 아래와 같이 발견되는 패턴들이 배열에 저장되었다.

 

이렇게 NUMBER 패턴이 배열에 만들어지면 실질적으로 첫번째 숫자의 위치를 알면 이를 기준으로 HEAD, NUMBER를 구별할 수 있다. 따라서 아래와 같이 사용한다.

 

// regex에 매치되는 첫 Index 찾기
    var numIndexA = a.indexOf(a.match(regexNum)[0]);
    var numIndexB = b.indexOf(b.match(regexNum)[0]);
    
    // Head 기준 정렬
    var sortByHead = a
      .substring(0, numIndexA)
      .toLowerCase()
      .localeCompare(b.substring(0, numIndexB).toLowerCase());

 

먼저 numIndexA의 값을 알아야한다. 이것은 a.indexOf(a.match(regexNum)[0]);이였는데 a.indexOf()의 파라미터로 들어간 값이 숫자 패턴들의 가장 앞의 숫자이기 때문에 NUMBER의 시작 숫자일 것이다.

따라서 a.indexOf를 하게 되면 현재 a는 a.file을 넣어줬으므로 file: value의 value에 해당하는 부분이고 이 value의 해당 시작 숫자가 처음 나타나는 위치를 반환하게 된다.

 

NUMBER 패턴의 시작 인덱스를 찾아냈으니 이를 기준으로 substring(0, numIndexA) ( = HEAD)를 이용해 a.file과 b.file을 비교하게 된다.

 

subString(start, End)

1. 리턴값은 잘라진 문자열. 즉 시작 인덱스부터 End 바로 앞까지 반환.
2. End값은 생략가능 (시작부터 끝까지)
3. start값이 End값보다 크다면 작은 숫자가 시작위치로 큰숫자가 종료위치로 셋팅된다.
4. 매개변수 둘 중에 한개값이 음수값이라면 시작위치가 0으로 설정된다.
5. 매개변수가 둘다 음수값이라면 반환값이 무조건 없다.

출처: https://webclub.tistory.com/20 [Web Club]

ex)

var string = '2013-06'

var year = string.substring(0,4) // 2013

 

이 sortByHead는 HEAD가 둘이 같으면 0, a가 b보다 더 작으면 -1, a가 b보다 크면 1이 되고 이 값으로 정렬이 가능하다. 

아래에서 이 값을 조건문에 이용한다.

// NOTE: head로 b를 그대로 둘지(1) b를 a와 바꿀지(-1) sortByHead로 판단한다. 같으면(=) num기준
    if (sortByHead === 0) {
      // Num기준 정렬
      var subStrA = parseInt(a.substring(numIndexA));
      var subStrB = parseInt(b.substring(numIndexB));
      if (subStrA < subStrB) {
        return -1;
      } else if (subStrA > subStrB) {
        return 1;
      } else {
        return 0;
      }
    } else if (sortByHead === -1) {
      return -1;
    } else {
      return 1;
    }

 

이제 var subStrA의 값이 어떻게 되는지 생각해보면, 

parseInt는 문자열과 숫자를 숫자로 변환해줄 수 있는 메서드이고 a.substring(numIndexA)를 파라미터로 받았으므로 이 a.substring(numIndexA)는 a.substring(2)로 END를 생략한 substring이기 때문에 시작(2)부터 끝까지를 반환해줄 것이다. 결과적으로 아래와 같은 값이 나온다. 

HEAD부분만 제거된 NUMBER+TAIL임을 알 수 있다. 이 값을 parseInt의 메서드에 파라미터로 받아주면 앞쪽의 정수값만 반환되어 저장된다!

이제 정수값을 가지고 sortByHead가 같은 경우에 한해서 만약  a가 더 작다고 판단되면 상대적으로 아래 있던 a의 위치를 변경해줘야 하므로 return -1을 해주고 a가 크다면 반대로 return 1을 한다. 같으면 return 0을 한다.

그 밖에 HEAD만으로 순서가 결정되는 경우는 그대로 a가 작아 -1이 나왔던 경우는 그대로 return -1을 해주고 a가 커서 1이 나왔던 부분은 그대로 return 1을 해준다.


answerWrap.sort((a, b) => {
    var result = compare(a.file, b.file);
        /***: HEAD가 같으면 index로 정렬해줘야 하는데
        /*** 문자 정렬과 동일한 방법으로 정렬하게 되면
        /*** 아스키코드 순으로 정렬되어 숫자의 크기대로 정렬되지 않음
        /*** 오름차순 정렬을 위해 a.index - b.index */
    return result === 0 ? a.index - b.index : result;
  });

위와 같은 과정을 거쳐 a,b가 서로 바꿔야 할지를 결정하는 0,1,-1이 compare에서 반환되어 result에 저장된다.

이 sort 함수 내에서는 역시 return 값에 의해 sort이 이루어진다. 

return 문을 보면 result 가 0이면 a.index - b.index를 반환하고 그게 아니면 result를 그대로 반환한다. 

 

예를 들어, 

이렇게 같은 배열 객체일 때 compare을 수행하면 0이 result로 나타나는 것을 볼 수 있다. 하지만 기존의 인덱스를 그대로 유지해주어야되므로 return result === 0 ? a.index - b.index : result; 를 보면 a.index(4) - b.index(3) = 1이므로 return 1이 되고 sort 함수에서 return 1은 compare함수에서와 같이 변경하지 않는 것을 뜻한다. 

변경하지 않아야만 기존에 아래에 있던(index가 4이므로 3에 비해 아래에 있어야 한다.) a가 그대로 위치할 것이고 이 순서를 그대로 유지하게 된다. 만약 a에 해당하는 배열 객체의 index가 b의 index보다 작다면 이것이 원래 순서에 맞지 않으므로 순서를 서로 변경시켜주는 것이다.

 

그리고 정렬이 다 끝나면 answerWrap의 answer(배열 객체)들을 map을 이용해서 answer.file(파일명) 부분만 return하여 answerWrap을 구성하도록 한 다음 return한다.

'Algorithm > ProblemSolving' 카테고리의 다른 글

[CodingTest] 프로그래머스 여행 경로 dfs  (0) 2020.12.25

댓글