문제
https://school.programmers.co.kr/learn/courses/30/lessons/17686
🐍파이썬
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/
💡 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 |