_.shuffle = function (arr) {
let arrCloned = arr.slice();
// 함수에 입력된 인자값을 수정하지 않고 arrCloned 변수에 인자값으로 입력받은 배열을 복사한다.
for (let fromIdx = 0; fromIdx < arr.length; fromIdx++) {
const toIdx = Math.floor(Math.random() * arr.length);
// toIdx는 (0 이상 1미만의 난수)와 (배열의 길이)를 곱한 값에서 소숫점 이하를 버린 값.
let temp = arrCloned[fromIdx];
// temp는 입력 배열의 n번째 요소. 반복문의 반복 횟수가 늘어날 수록 n번째는 점진적 증가.
arrCloned[fromIdx] = arrCloned[toIdx];
// 입력 배열의 n번째 요소는 입력 배열의 i번째 요소
arrCloned[toIdx] = temp;
// 입력 배열의 i번째 요소는 입력 배열의 n번째 요소
}
return arrCloned;
};
순서를 섞을 때에는 순서를 이리저리 랜덤하게 섞어야 한다.
위 코드에서는 순서를 랜덤하게 배정하기 위해 Math.floor(Math.random() * arr.length) 를 사용했다.
Math.random()은 0보다 크고 1보다 작은(0 < n < 1) 임의의 난수를 생성하는 js 내장 메소드이다. (반복문이 반복할때마다 난수를 새로 생성한다)
이렇게 생성한 난수에 배열의 길이를 곱해주어야 하는데,
배열의 index를 뒤섞어 주어야 하기 때문에 배열의 길이를 곱하여 배열의 길이만큼만 분리할 수 있도록 한다.
난수에 배열의 길이를 곱하여 생긴 숫자는, 인덱스 지정을 할 수 있도록 Math.floor()를 이용하여 소숫점 이하를 버린다.
순서 할당의 key point
배열의 길이를 곱해주어 임의의 index가 배열의 길이만큼만 생성되도록 한다Math.floor(Math.random() * arr.length) arr.length가 3일 경우 : 1~2 생성 arr.length가 5일 경우 : 1~4 생성 arr.length가 7일 경우 : 1~6 생성 arr.length가 9일 경우 : 1~8 생성이와 같이
arr.length -1의 임의의 값을 생성하기 때문에 셔플에 쓰기 좋다.
반복문이 돌면서 toIdx에서 생성한 랜덤 인덱스를 이용하여 배열의 순서를 섞을 차례이다.
입력 받은 배열을 0번째 인덱스부터 마지막까지 순차적으로 순서를 바꾸기 위해
let temp = arrCloned[fromIdx]
위와 같이 temp라는 변수에 배열의 fromIdx번째 인덱스를 할당한다.
fromIdx는 반복문의 조건에서도 볼 수 있듯, 0부터 arr.length -1까지 점진적으로 증가한다.
그리고 arrCloned[fromIdx] = arrCloned[toIdx]를 이용하여 위치를 재할당 하고, toIdx번째 위치에 있던 요소를 fromIdx번째 위치로 재할당하여 옮긴다.
뒤섞기의 key point
얼핏 보면 2번의 재할당을 왜 하는지 이해하기 어려울 수 있지만, 작동하는 로직을 이해하면 쉽다.let temp = arrCloned[fromIdx] arrCloned[fromIdx] = arrCloned[toIdx] arrCloned[toIdx] = temp이 코드에서 하는 역할은 간단하다.
fromIdx번째 요소와toIdx번째 요소를 맞바꾸는 것이다.fromIdx가 1이고 toIdx가 3일때, let temp = arrCloned[1] arrCloned[1] = arrCloned[3] arrCloned[3] = arrCloned[1]이런 식으로 간단하게 각 인덱스의 요소를 맞바꾸어 주는 것 뿐이다.
코드는 어렵게 생각할 것 없이 로직만 잘 생각하면 술술 써내려 갈 수 있다.