[프로그래머스 lev2/JS] 파일명 정렬

woolee의 기록보관소·2022년 11월 15일
0

알고리즘 문제풀이

목록 보기
96/178

문제 출처

프로그래머스 lev2 - 파일명 정렬

나의 풀이

1차 시도 (45/100)

function solution(files) {
  let head = {};
  let num = {};
  let answer = [...files];
  let result = [];

  for (let i = 0; i < files.length; i++) {
    let s = 0;
    for (let j = 0; j < files[i].length; j++) {
      if (isNaN(files[i][j]) && !isNaN(files[i][j + 1])) {
        head[i] = files[i].slice(0, j + 1).toLowerCase();
        s = j + 1;
      } else if (!isNaN(files[i][j]) && isNaN(files[i][j + 1])) {
        num[i] = Number(files[i].slice(s, j + 1));
        break;
      }
    }
  }

  let mapped = answer.map((el, i) => {
    return { idx: i, val: el };
  });

  mapped.sort((a, b) => {
    if (head[a.idx] > head[b.idx]) return 1;
    if (head[a.idx] === head[b.idx]) {
      if (num[a.idx] > num[b.idx]) return 1;
      if (num[a.idx] === num[b.idx]) return 0;
      if (num[a.idx] < num[b.idx]) return -1;
    }
    if (head[a.idx] < head[b.idx]) return -1;
  });

  mapped.forEach((el) => {
    result.push(el.val);
  });

  return result;
}

console.log(
  solution([
    "img12.png",
    "img10.png",
    "img02.png",
    "img1.png",
    "IMG01.GIF",
    "img2.JPG",
  ])
);

2차 시도 (통과)

isNaN()으로 숫자인지 문자인지 판별을 했는데,
문제는 공백 문자열도 false로 판별한다는 점이었다.
그래서 공백 문자열을 만나면 while문을 순회하면서 공백 문자열을 처리해주었다. 가뜩이나 지저분한 풀이가 더 지저분해졌다.

// 1.
2중 for문을 순회하면서,
j와 j+1 사이에 문자열로 숫자로 나뉘는 지점을 찾는다. 이때 공백문자열을 만나면 while문을 통해서 진짜 숫자를 만날 때까지 j++해준다.

-->> j가 문자고, j+1이 숫자면 head에 넣는다. 이때 key값을 i index로 설정한다. (넣을 때 소문자로 치환해서 넣는다.)
-->> j가 숫자이고, j+1이 문자면 num에 넣는다. 이때도 key값을 i index로 설정한다. (넣을 때 숫자로 치환해서 넣는다.)

// 2.
sort 함수를 쓰기 전에 sort 함수에서 각 a,b의 index를 쓰고 싶어서 맵핑을 진행했다.

// 3.
sort를 돌리면서 head[a.idx]와 head[b.idx]를 비교하면서 오름차순으로 정렬하되, 둘이 같으면 숫자로 비교하기 위해 num[a.idx]와 num[b.idx]을 비교한다. 숫자마저 같으면 0을 return 해서 순서를 건드리지 않는다.

// 4.
mapped를 순회하면서 val 값을 result 배열에 push해준다.

function solution(files) {
  let head = {};
  let num = {};
  let answer = [...files];
  let result = [];

  // 1.
  for (let i = 0; i < files.length; i++) {
    let s = 0;
    for (let j = 0; j < files[i].length; j++) {
      if (isNaN(files[i][j]) && !isNaN(files[i][j + 1])) {
        if (files[i][j + 1] === " ") {
          while (!isNaN(files[i][j + 1]) && files[i][j] !== " ") {
            j++;
          }
        }
        head[i] = files[i].slice(0, j + 1).toLowerCase();
        s = j + 1;
      } else if (!isNaN(files[i][j]) && isNaN(files[i][j + 1])) {
        if (files[i][j] === " ") {
          while (!isNaN(files[i][j]) && files[i][j + 1] !== " ") {
            j++;
          }
        }
        num[i] = Number(files[i].slice(s, j + 1));
        break;
      }
    }
  }

  // 2. 
  let mapped = answer.map((el, i) => {
    return { idx: i, val: el };
  });

  // 3.
  mapped.sort((a, b) => {
    if (head[a.idx] > head[b.idx]) return 1;
    if (head[a.idx] === head[b.idx]) {
      if (num[a.idx] > num[b.idx]) return 1;
      if (num[a.idx] === num[b.idx]) return 0;
      if (num[a.idx] < num[b.idx]) return -1;
    }
    if (head[a.idx] < head[b.idx]) return -1;
  });

  // 4.
  mapped.forEach((el) => {
    result.push(el.val);
  });

  return result;
}

console.log(
  solution([
    "img  12.png",
    "img10.png",
    "img02.png",
    "img1.png",
    "IMG01.GIF",
    "img2.JPG",
  ])
);

다른 풀이

localCompare 메서드를 사용한 풀이

localeCompare(compareString, locales, options)
첫번째 인자로 비교 문자열을 넣어준다.
A.localeCompare(정규표현식) 이렇게 사용하면 될듯.

정규표현식은 아직 너무 어렵다..
\D : 숫자가 아닌 것, * : 반복, () : 그룹처리 이므로
=> (\D*) : 숫자가 아닌 그룹
반대로
=> ([0-9]*) : 숫자인 그룹
i는 플래그. ignore case를 표현하여 대상 문자열에 대해 대/소문자 식별하지 않는다는 의미를 갖는다.

A,B[1]은 head를 표현,
A,B[2]는 number를 표현

function solution(files) {
    let answerWrap = files.map((file, index) => ({file, index}));
    const compare = (a, b) => {
      const reg = /(\D*)([0-9]*)/i;
      const A = a.match(reg);
      const B = b.match(reg);
      const compareHead = A[1].toLowerCase().localeCompare(B[1].toLowerCase());
      const compareNumber = (a, b) => {
         return parseInt(a) > parseInt(b) ? 
            1 : parseInt(a) < parseInt(b) ? 
             -1 
            : 0
      }
      return compareHead === 0 ? compareNumber(A[2], B[2]) : compareHead
    }
    answerWrap.sort((a, b) => {
      const result = compare(a.file, b.file);
      return result === 0 ? a.index - b.index : result;
    })
    return answerWrap.map(answer => answer.file);
}

정규표현식을 활용한 풀이 2

+도 반복을 표현함. 1번 이상의 반복

destructuring assignment를 사용해 [fn, head, num]으로 정리

function solution(files) {
    const re = /^([a-zA-Z-\. ]+)([0-9]+)(.*)$/
    let dict = []
    files.forEach((entry, idx) => {
        let [fn, head, num] = entry.match(re)
        dict.push({fn, head: head.toLowerCase(), num: parseInt(num), idx})
    })

    return dict.sort((a, b) => {
        if (a.head > b.head) return  1
        if (a.head < b.head) return -1
        if (a.num > b.num) return  1
        if (a.num < b.num) return -1
        return a.idx - b.idx
    }).map(e => e.fn)
}
profile
https://medium.com/@wooleejaan

0개의 댓글