본문 바로가기
Problem Solving/프로그래머스

[프로그래머스 | 파이썬 / 자바스크립트] [3차] 파일명 정렬(2018 KAKAO BLIND RECRUITMENT / level 2)

by 청량리 물냉면 2023. 3. 15.
반응형
문제

https://school.programmers.co.kr/learn/courses/30/lessons/17686

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

 

🐍파이썬
def solution(files):
    answer = []
    head = ''
    number = ''
    tail = ''
    for i in files:
        for j in range(len(i)):	#파일의 문자를 하나씩 확인
            if i[j].isdigit():	#숫자가 나오면
                head = i[:j]	#숫자 앞까지 끊어서 head
                number = i[j:]	#숫자부터는 number
                for k in range(len(number)):	#number/tail 구분
                    if number[k] in [" ", ".", "-"]:	#구분자가 나오면
                        tail = number[k:]	#구분자부터 tail
                        number = number[:k]	#구분자 이전은 number, number를 먼저 변경해버리면 tail값이 이상하게 나오므로 주의
                        break	#그 이후에 나오는 구분자는 처리하지 않기 위해 break
                answer.append([head, number, tail])	#head, number, tail값이 처리되면 이를 배열에 저장
                head = ''	#다음 for문을 위해 변수 초기화
                number = ''
                tail = ''
                break	#다음 문자가 숫자일 경우 반복문이 또다시 실행되므로 실행중지를 위해 break
    answer.sort(key = lambda x:(x[0].lower(), int(x[1])))	#head값을 기준으로 정렬 후 number값을 기준으로 정렬
    return [''.join(i) for i in answer]

거의 대부분 풀이를 참고했다...

 

 

다른 풀이 방법

import re
def solution(files):
    a = sorted(files, key=lambda file : int(re.findall('\d+', file)[0]))
    b = sorted(a, key=lambda file : re.split('\d+', file.lower())[0])
    return b

정규표현식 사용

re모듈

findall(패턴, 문자열, 플래그)

문자열 안의 패턴에 맞는 케이스를 전부 찾아서 리스트로 반환

참고: https://brownbears.tistory.com/506


def head_compare(data):    
    route = 0
    answer = ['', '']
    for i, v in enumerate(data):
        if (route == 0):
            if (v.isnumeric() == True):	#숫자일 때
                answer[0] = data[:i].lower()	#head
                route = 1
                answer[1] += v	#현재 문자를 저장(number)
        elif (route == 1):
            if (v.isnumeric() == True): #숫자일 경우
                answer[1] += v  #현재 문자를 저장(number)
            else:   #숫자가 아닐 경우(tail)
                break   #반복문 종료
    answer[1] = int(answer[1])  #문자열을 숫자로 변경
    return answer
    # answer = [['f-', 5], ['b-', 50], ['a-', 10], ['f-', 14]]

def solution(files):
    answer = []
    files.sort(key = lambda x:head_compare(x))
    return files

 

 

🐥자바스크립트
더보기

❌ 오답

function solution(files) {
    let sortArr = [];
    for(let i = 0; i < files.length; i++){
        let answer = ['', '', '', i]
        for(let j = 0; j < files[i].length; j++){
            if(isNaN(files[i][j]) === false){
                answer[0] = files[i].slice(0, j);
                answer[1] = files[i].slice(j);
                for(let k = 0; k < answer[1].length; k++){
                    if(answer[1][k] === " " || answer[1][k] === "." || answer[1][k] === "-"){
                        answer[2] = answer[1].slice(k);
                        answer[1] = answer[1].slice(0, k);
                        break;
                    }
                }
                sortArr.push(answer);
                break;
            }
        }
    }
    return sortArr.sort((a, b)=>{
        if(a[0].toLowerCase() > b[0].toLowerCase()){
            return 1;
        } else if(a[0].toLowerCase() < b[0].toLowerCase()){
            return -1;
        } else if(parseInt(a[1]) - parseInt(b[1]) !== 0){
            return parseInt(a[1]) - parseInt(b[1]);
        } else {
            return a[3] - b[3];
        }
    }).map((i) => [i[0], i[1], i[2]].join(''));
}
function solution(files) {
    let sortArr = [];
    for(let i = 0; i < files.length; i++){
        let answer = ['', '', '', i]	//head, number, tail, index 담는 변수
        for(let j = 0; j < files[i].length; j++){
            if(isNaN(parseInt(files[i][j])) === false){
                answer[0] = files[i].slice(0, j);	//0~j-1번째 인덱스까지 head
                answer[1] = files[i].slice(j);	//j~끝 인덱스까지 number
                for(let k = 0; k < answer[1].length; k++){	//number에서 number, tail분리
                    if(answer[1][k] === " " || answer[1][k] === "." || answer[1][k] === "-"){	//특수기호 뒤는 tail
                        answer[2] = answer[1].slice(k);
                        answer[1] = answer[1].slice(0, k);	//특수기호 앞까지 number
                        break;
                    }
                }
                sortArr.push(answer);	//[head, number, tail, index]를 sortArr에 push
                break;
            }
        }
    }
    return sortArr.sort((a, b)=>{
    	//head 오름차순 정렬
        if(a[0].toLowerCase() > b[0].toLowerCase()){
            return 1;
        } else if(a[0].toLowerCase() < b[0].toLowerCase()){
            return -1;
        //number 오름차순 정렬
        } else if(parseInt(a[1]) - parseInt(b[1]) !== 0){
            return parseInt(a[1]) - parseInt(b[1]);
        //head, number 동일 시 index값으로 오름차순 정렬(들어온 시간 순서대로 정렬)
        } else {
            return a[3] - b[3];
        }
    //index값을 제외하고 join해서 맵핑 후 출력
    }).map((i) => [i[0], i[1], i[2]].join(''));
}

 

➕ 아래와 같이 index를 추가하지 않아도 답으로 인정된다.

function solution(files) {
    let sortArr = [];
    for(let i = 0; i < files.length; i++){
        let answer = ['', '', '']	//head, number, tail 담는 변수
        for(let j = 0; j < files[i].length; j++){
            if(isNaN(parseInt(files[i][j])) === false){
                answer[0] = files[i].slice(0, j);	//0~j-1번째 인덱스까지 head
                answer[1] = files[i].slice(j);	//j~끝 인덱스까지 number
                for(let k = 0; k < answer[1].length; k++){	//number에서 number, tail분리
                    if(answer[1][k] === " " || answer[1][k] === "." || answer[1][k] === "-"){	//특수기호 뒤는 tail
                        answer[2] = answer[1].slice(k);
                        answer[1] = answer[1].slice(0, k);	//특수기호 앞까지 number
                        break;
                    }
                }
                sortArr.push(answer);	//[head, number, tail]을 sortArr에 push
                break;
            }
        }
    }
    return sortArr.sort((a, b)=>{
    	//head 오름차순 정렬
        if(a[0].toLowerCase() > b[0].toLowerCase()){
            return 1;
        } else if(a[0].toLowerCase() < b[0].toLowerCase()){
            return -1;
        //number 오름차순 정렬
        } else if(parseInt(a[1]) - parseInt(b[1]) !== 0){
            return parseInt(a[1]) - parseInt(b[1]);
        //head, number 동일 시 기존순서 유지
        } else {
            return 0;
        }
    //배열의 값을 join해서 맵핑 후 출력
    }).map((i) => i.join(''));
}
if(isNaN(parseInt(files[i][j])) === false)​

parseInt 하나때문에 문제 푸는 데 1시간 반 이상을 더 소요했다...
왜 isNaN만 사용했을 때는 오류가 뜨는지 궁금해서 parseInt와 isNaN의 차이점을 찾아보았다.

💡 parseInt와 isNaN의 차이점
1️⃣ isNaN takes an integer as an argument - therefore JS converts "" to 0
(isNaN은 정수를 인수로 가진다. - 따라서 인수의 ""(빈 문자열)은 0으로 취급되어 false가 리턴된다.)
2️⃣ parseInt takes a string as an argument - therefore an empty string is not a number
(parseInt는 string을 인수로 가진다 - 따라서 인수의 ""(빈 문자열)은 숫자로 취급되지 않는다.)
출처: https://stackoverflow.com/questions/8271836/isnan-vs-parseint-confusion


💡 isNaN함수 사용시 주의할 점

//isNaN 단독 사용 시
console.log(isNaN(" ")); // false
console.log(isNaN(".")); // true
console.log(isNaN("number")); // true
console.log(isNaN("2.19")); // false
console.log(isNaN("")); // false
console.log(isNaN(null)); // false

//parseInt로 감싼 경우
console.log(isNaN(parseInt(" "))); // true
console.log(isNaN(parseInt("."))); // true
console.log(isNaN(parseInt("number"))); // true
console.log(isNaN(parseInt("2.19"))); // false
console.log(isNaN(parseInt("")));	// true
console.log(isNaN(parseInt(null)));	// true
isNaN 함수의 인수가 Number 형이 아닌 경우, 그 값은 먼저 숫자로 강제됩니다. 결과값은 그 뒤에 NaN인지 결정하기 위해 테스트됩니다. 따라서 숫자 형으로 강제된 결과 유효한 비NaN 숫자값(특히 강제될 때 숫자값이 0 또는 1을 주는 빈 문자열 및 boolean 원시형)이 되는 비숫자의 경우, "false" 반환값은 예기치 않을 수 있습니다;
(출처: https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/isNaN)
(추가로 참고하면 좋을 블로그: https://mygumi.tistory.com/335)

👉 다양한 테스트 케이스에서 숫자가 아닌 원소가 false를 리턴하여 오류가 생긴 듯 하다...

 

 

다른 풀이 방법

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());	//head 비교(오름차순)
      const compareNumber = (a, b) => {	//number 비교(오름차순)
         return parseInt(a) > parseInt(b) ? 
            1 : parseInt(a) < parseInt(b) ? 
             -1 
            : 0
      }
      return compareHead === 0 ? compareNumber(A[2], B[2]) : compareHead	//head동일할 경우 number로 정렬
    }
    answerWrap.sort((a, b) => {
      const result = compare(a.file, b.file);
      return result === 0 ? a.index - b.index : result;	//head, number 동일할 경우 index로 정렬
    })
    return answerWrap.map(answer => answer.file);
}

정규표현식을 사용한 풀이.

정규표현식 참고 블로그: https://heropy.blog/2018/10/28/regexp/

 

정규표현식, 이렇게 시작하자!

매일 쓰는 것도, 가독성이 좋은 것도 아니지만, 모르면 안되는 정규표현식. 저는 이렇게 공부하기 시작했습니다! (자바스크립트를 기준으로 설명합니다)

heropy.blog

 

💡 match()

문자열이 정규식과 매치되는 부분을 검색하는 메서드

 

💡 localeCompare()

참조 문자열이 정렬 순으로 지정된 문자열 앞 혹은 뒤에 오는지 또는 동일한 문자열인지 나타내는 수치를 반환하는 메서드

localeCompare(compareString)
localeCompare(compareString, locales)
localeCompare(compareString, locales, options)

매개변수

  • compareString: referenceStr가 비교되는 문자열
  • locales: 기준 언어(독일어, 스웨덴어 등...)
  • options: localeCompare()이 제공하는 결과를 options를 통해 사용자 정의 가능

반환 값

compareString 전에 referenceStr가 위치하는 경우 음수, compareString 후에 referenceStr가 위치하는 경우 양수, 동등할 경우 0

 

반응형