String
과 Array
의 전환for
문보다는 forEach
로 코드를 작성하고 싶을 때 유용함.let my_str = 'hello';
console.log([...my_str]);
//결과: > Array ["h", "e", "l", "l", "o"]
Array.prototype.join()
과 String.prototype.split()
사용하기String.prototype
에는 Array
처럼 reverse
메소드가 없음. 그래서 이 때 배열로 전환해서 뒤집고 다시 문자열로 바꿔줄 때 유용💡
String.prototype.split()
split() 메서드는 String 객체를 지정한 구분자를 이용하여 여러 개의 문자열로 나눠서 그걸 담은 Array로 반환합니다.
💡
Array.prototype.join()
join() 메서드는 배열의 모든 요소를 연결해 하나의 문자열로 만듭니다.
이 때, 추가적으로 separator라는 매개변수를 넘길 수 있는데, 넘겨주면 하나의 문자열로 이어붙일 때 해당 separator를 끼워넣으면서 붙여진다.
Array.prototype.join()
사용 예시const elements = ['Fire', 'Air', 'Water'];
console.log(elements.join('-'));
// expected output: "Fire-Air-Water"
split("")
→ reverse()
→ join("")
)function getReverseStr(str) {
return str.split("").reverse().join("");
}
JavaScript는 다른 언어와 다르게 2차원 배열 이상의 개념이 존재하지 않아서 처음부터 한번에 const arr = [][];
와 같은 식으로 2차원으로 초기화하거나 선언할 수 없다. (물론 "한 번에" 2차원 배열을 만들어낼 수 없는 것이지 아예 2차원 배열을 사용할 수 없는 것은 아님.)
우선 2차원 배열을 선언 및 초기화 하는 방법을 알아보자.
Array.from()
활용하기Array.from()
메서드는 유사 배열 객체(array-like object)나 반복 가능한 객체(iterable object)를 얕게 복사해 새로운 Array
객체를 만듭니다.
arrayLike
: 배열로 변환하고자 하는 유사 배열 객체나 반복 가능한 객체mapFn
: 배열의 모든 요소에 대해 호출할 맵핑 함수/* 사용 예시 */
console.log(Array.from('foo'));
// Expected output: Array ["f", "o", "o"]
console.log(Array.from([1, 2, 3], x => x + x));
// Expected output: Array [2, 4, 6]
만약 [4, 5]짜리 2차원 배열을 만들고 싶다면
Array.from(Array(4), () => Array(5));
그런데 여기에서 모두 다 0으로 초기화하고 싶다면 Array.prototype.fill()
메소드를 체이닝 시키면 된다.
Array.from(Array(4), () => Array(5).fill(0));
String.prototype.slice()
활용하기- 상황 예시
`new_id`의 길이가 16자 이상이면, new_id의 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다.
이 때는 굳이 if(new_id.length >= 16) 어쩌구저쩌구
하지 말고, 그냥 어차피 최대 15개까지만 문자를 가져가면 되는거니까 new_id.slice(0, 15)
로 해도 상관이 없다! (15가 해당 문자열의 길이보다 넘쳐도 상관 없음)
깔끔한 코드를 쓰자!
String
에서 Number
로 전환하는 방법String
에서 Number
로 전환하는 방법에는 다음과 같이 세 가지가 존재한다.
parseInt()
Math.floor()
~~
연산자그런데, 코딩테스트에서 시간 초과를 최대한 방지하고 싶다면 ~~
연산자를 쓰는 것이 매우매우 유리하다. 세 가지 방법 중에서 성능이 제일 좋고 시간이 적게 들기 때문!!
(참고: https://developer-alle.tistory.com/393)
Math.floor()
보다는 우측 시프트 연산자를 0만큼 이동하는 것을 사용하는 것이 더 좋다.
ex) (5 / 2) >> 0
>> 는 비트연산자인데 자바스크립트 내장 객체 Math.floor() 를 이용하는 것 보다 더 빠른 성능을 보입니다.
우측 시프트 연산자(>>)를 0만큼 이동하면 이는 소수점 아래를 버리는 것과 동일한 기능을 합니다.
String.prototype.substring()
활용하기사용방법
str.substring(indexStart[, indexEnd])
인자값
indexStart
: 반환문자열의 시작 인덱스indexEnd
: 옵션. 반환문자열의 마지막 인덱스 (포함하지 않음.)반환값
String.prototype.slice()
활용하기사용방법은 String.prototype.substring()
과 같다.
Array.prototype.filter()
활용하기해당 방법은 원하는 요소 A의 개수만! 세는 방법이다.
const arr = [1, 2, 3, 1, 1, 1, 3, 4, 5];
//만약 1의 개수를 세고 싶다면,
let cnt = arr.filter(elem => elem === 1).length;
Map
을 활용한 방법이 방법은 배열에 등장하는 모든 요소에 대해서 각각의 개수를 모두 기록하는 방법이다. (위의 방법이랑은 다르다)
Map.prototype.set()
메소드와 Map.prototype.get()
메소드를 활용한다.Map.prototype.get()
메소드를 사용할 때 존재하지 않는 key값에 대한 리턴값은 undefined
이고, 이를 원하는 값
과 ||
연산을 통해서 새로운 key 값을 만들면서 value를 설정해 줄 수 있다.만약 0부터 카운트하고 싶다면 아래 코드를 참고하자.
const arr = [1, 2, 3, 1, 1, 1, 3, 4, 5];
var map = new Map();
arr.forEach(num => {
map.set(num, (map.get(num) || 0) + 1);
});
Array.prototype.some()
을 활용한 방법1. 인쇄 대기목록의 가장 앞에 있는 문서(J)를 대기목록에서 꺼냅니다.
2. 나머지 인쇄 대기목록에서 J보다 중요도가 높은 문서가 한 개라도 존재하면 J를 대기목록의 가장 마지막에 넣습니다.
3. 그렇지 않으면 J를 인쇄합니다.
💡
Array.prototype.some()
some() 메서드는 배열 안의 어떤 요소라도 주어진 판별 함수를 적어도 하나라도 통과하는지 테스트합니다. 만약 배열에서 주어진 함수가 true을 반환하면 true를 반환합니다. 그렇지 않으면 false를 반환합니다. 이 메서드는 배열을 변경하지 않습니다.
Array.prototype.sort()
을 활용한 방법JavaScript에서 Array.prototype.sort()
메소드를 사용할 것이라면 자신이 원하는 대로 정렬하기 위해서는 반드시 직접 compareFunc를 구현해야한다는 함정이 숨어있다.
sort
메소드에 따로 compareFunc를전달해주지 않으면 배열의 모든 요소를 문자열로 변환하고 유니 코드 코드 포인트 순서로 문자열을 비교하여 정렬된다. 그래서 만약에 1, 8, 2, 15]
라는 배열이 있다고 해도 아무런 compareFunction 없이 sort()를 쓰게 된다면 문자 순서대로 [1, 15, 2, 8]
나오게 될 것이다.
따라서, 우리가 원하는 대로! 숫자의 정렬을 하기 위해서는 적절한 compareFunc를 넘겨주어야 하는데, 오름차순으로 정렬하기 원하는 경우 (a, b) => a - b
라는 화살표함수를, 내림차순으로 정렬하기 원하는 경우 (a, b) => b - a
라는 화살표함수를 전달해주면 된다.
그냥 a, b라고 하면 헷갈리므로 다음과 같이 이해하면 쉽겠다!
우선 Array.prototype.sort()
메소드가 어떤 원리로 돌아가는지 살펴보도록 하자.
sort
메소드는 compareFunc
가 리턴하는 값에 따라 정렬되는 방식이 다르다.
1. 0을 리턴한 경우 → 아무런 변동 X
2. 양수를 리턴한 경우 → 오름차순
3. 음수를 리턴한 경우 → 내림차순
다음의 코드를 보자. (참고한 블로그: https://rkdvnfma90.tistory.com/232)
const arr = [1, 2, 3];
arr.sort((a, b) => b - a);
이렇게 있을 때, 파라미터 a, b에 각각 1, 2가 들어오겠다고 생각을 하겠지만, 사실은 2, 1이 들어온다. 그렇기 때문에 이 부분이 헷갈린다면 다음과 같이 이해하면 되겠다.
arr.sort((next, prev) => prev - next);
💡 유클리드 호제법이란
두 수 A, B에 대해서 A > B 이고, A를 B로 나눈 나머지를 r이라고 할 때,GCD(A, B) == GCD(B, r)
과 같다는 원리이다. (r이 0이라면 그 때의 B가 최대공약수이다.)
코드를 한 줄로 작성하면 다음과 같다.
const GCD = (num1, num2) => (num2 > 0 ? GCD(num2, num1 % num2) : num1);
여러 줄로 작성하면 다음과 같다.
function GCD(num1, num2) {
if(num2 === 0) return num1;
else return GCD(num2, num1 % num2);
}
주의할 점은, GCD 함수를 사용할 때 num1
> num2
가 보장되도록 처리하여 넣어주어야 한다.
Set을 생성할 때, 생성자에 배열을 전달할 수 있으며, 이때, 모든 중복이 제거된 set 인스턴스가 생성된다.
이 set을 다시 배열로 변환하여 반환하고 싶다면, 다음과 같이 Spread Operation을 통하여 다시 배열로 변환할 수 있다.
const duplicatedArr = [1, 2, 1, 2, 3, 4, 5];
const mySet = new Set(duplicatedArr);
const uniqueArr = [...mySet]; // [1, 2, 3, 4, 5];
Array.prototype.from()
사용하기const duplicatedArr = [1, 2, 1, 2, 3, 4, 5];
const mySet = new Set(duplicatedArr);
const uniqueArr = Array.from(mySet);
💡 Set 이해하기
Set는 순서가 없는, 중복되지 않은 데이터의 집합이다. Array와의 차이점을 이해하는 것이 무엇보다 중요하다.
배열(Array)는 데이터를 순서있게 저장한다. 그래서 인덱스(index)를 통해서 특정 위치에 저장되어 있는 데이터에 접근이 가능하다. 그리고 배열에는 동일한 값을 여러 번 저장할 수 있다. 값이 동일하더라도 인덱스가 틀리기 때문에 데이터의 중복이 문제되지 않는다.
반면에 Set는 얼핏 보기에는 배열과 비슷해보일 수 있지만 사실 결이 아주 다른 자료구조이다. 우선 세트는 데이터를 순서없이 저장한다. 따라서 배열처럼 인덱스를 통해서 접근할 수가 없다.
그리고 세트는 중복된 데이터를 허용하지 않는다. 즉, 기존에 세트에 있는 값을 또 추가해봤자 아무런 일도 생기지 않는다.
문제에 따라서 DFS가 더 어울리는 유형이 있고, BFS가 더 어울리는 문제가 있다.
(출처: tsi0521님의 Velog)
재귀함수
와 함께 사용한다.Queue
와 함께 사용한다.Array.prototype.shift()
로 하다가 시간 초과가 나는 것 같다면, 이 문제에서 해결한 방식처럼 queueIdx를 따로 둬서 실제로 shift하진 않지만 shift 한 효과를 내도록 해보자.특이한 형태로 들어오는 배열의 형태를 그대로 가져오면서 내가 원하는 값들로 초기화해주고 싶을 때, 얕은 복사를 하게 되면 원본 배열도 바뀌어버린다. 이 때 다음과 같은 방법들을 사용하면 깊은 복사가 되어 원본이 변경되지 않는다.
JSON.stringify()
, JSON.parse()
사용하기//피라미드 형태의 삼각형을 표현하는 특이한 모양의 배열
const triangle = [[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]];
//저 형태를 그대로 따서 0으로 초기화하면서, 원본 배열은 변경하고 싶지 않을 때
const cache = JSON.parse(JSON.stringify(triangle));
cache.forEach(arr => {
for(let i=0; i < arr.length; i++) arr[i] = 0;
});
console.log(triangle); // [[7], [3, 8], [8, 1, 0], [2, 7, 4, 4], [4, 5, 2, 6, 5]]
console.log(cache); // [[0], [0, 0], [0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0, 0]]
답변을 먼저 제출하여, 효율성 테스트를 채점하는지 우선 확인합니다.
효율성을 채점하지 않는다면, 대부분 알고리즘이 10초이내에 돌아가게 만들면 됩니다.
그 다음, 문제의 제한 사항을 확인합니다.
여기서에서 배열을 총 길이들을 확인하여, 최대 가능한 연산횟수를 대충 유추할 수 있습니다.예를 들어 [이모티콘 할인행사] 문제에서 users가 최대 100, emoticons가 최대 7의 길이를 갖습니다.
또한, 문제에서 할인율은 [10, 20, 30, 40] 4종류의 할인율만 가진다고 하였습니다.
대충 가질 수 있는 할인율을 계산해보자면, 약 16000(4의 7승)가지가 될겁니다.
그럼,100 * 7 * 16000 = 1100
만번의 연산을 수행합니다.
여기서 보조 연산이 들어가거나, 언어의 속도에 따라 추가적인 시간 소요가 일어납니다.
많은 연산이기는 하지만, 무지성으로 탐색해도, 1억번을 넘지 않으므로, 충분히 시간안에 통과가 가능합니다.
연산횟수가 1억 단위가 넘어가면, 시간 초과가 뜰가능성이 존재한다는 사실을 늘 염두하셔야 합니다.
이런 팁을 알면, 실제 코딩테스트를 풀 때, 사용할 알고리즘을 빠르게 유추해볼 수 있겠습니다.
(출처: 전현서님)