문제
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]
거의 대부분 풀이를 참고했다...
- 참고한 풀이: https://tinyurl.com/2zvfsnhu
- 람다 참고 블로그: https://velog.io/@aonee/Python-%EC%A0%95%EB%A0%AC-sort-sorted-reverse
다른 풀이 방법
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
정규표현식 사용
- '\d+' : 하나 혹은 그 이상 연결된 숫자
- 정규표현식 정리 블로그: https://tinyurl.com/2g2omnte
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 함수의 인수가 Number 형이 아닌 경우, 그 값은 먼저 숫자로 강제됩니다. 결과값은 그 뒤에 NaN인지 결정하기 위해 테스트됩니다. 따라서 숫자 형으로 강제된 결과 유효한 비NaN 숫자값(특히 강제될 때 숫자값이 0 또는 1을 주는 빈 문자열 및 boolean 원시형)이 되는 비숫자의 경우, "false" 반환값은 예기치 않을 수 있습니다;
//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
(출처: 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(compareString) localeCompare(compareString, locales) localeCompare(compareString, locales, options)
매개변수
- compareString: referenceStr가 비교되는 문자열
- locales: 기준 언어(독일어, 스웨덴어 등...)
- options: localeCompare()이 제공하는 결과를 options를 통해 사용자 정의 가능
반환 값
compareString 전에 referenceStr가 위치하는 경우 음수, compareString 후에 referenceStr가 위치하는 경우 양수, 동등할 경우 0
'Problem Solving > 프로그래머스' 카테고리의 다른 글
[프로그래머스 | 파이썬 / 자바스크립트] 추억 점수(연습문제 / level 1) (0) | 2023.03.31 |
---|---|
[프로그래머스 | 파이썬 / 자바스크립트] 평행(코딩테스트 입문 / level 0) (0) | 2023.03.16 |
[프로그래머스 | 파이썬 / 자바스크립트] 다리를 지나는 트럭(스택/큐 / level 2) (0) | 2023.03.16 |
[프로그래머스 | 파이썬 / 자바스크립트] 덧칠하기(연습문제 / level 2) (0) | 2023.03.12 |
[프로그래머스 | 파이썬 / 자바스크립트] 겹치는 선분의 길이(코딩테스트 입문/ level 0) (0) | 2023.03.12 |
[프로그래머스 | 파이썬 / 자바스크립트] [3차] 압축(2018 KAKAO BLIND RECRUITMENT/ level 2) (2) | 2023.03.11 |