메소드를 정리하다가, Koans 빡공했더니 스코프 ( Scope ), 클로져 ( closure ), 클래스 ( class ), 인스턴스 ( instance ), 상속 ( inheritance ) 등등 설명할 게 점점 늘어나는 걸 느꼈다. 특히, 클래스부터 상속까지의 개념을 하고 나니, 메소드를 설명할 때에 바로 클래스로 보여줬으면 기존에 사용하던 메소드처럼 사용하는 모습을 보여줄 수 있었을 텐데! 하면서 조금 아쉬웠지만, 처음부터 공부하는 사람이 보는 내용에 난데없이 클래스가 등장하면 혼란스러울 것 같아서 이번 포스트까지는 기존 방식대로 예시를 작성하겠다. 대신에 다음 포스트는 스코프와 클로져에 대해 포스팅하고 이후 클래스까지 설명을 하고 나면, 클래스로 이전에 만들었던 나만의 함수들을 만들어 보겠다! 말은 쉽지만 시간이 엄청나게 소요될 거 같다.... 벌써 좀 무섭네...
// 배열의 요소를 대체하기
splice()
// 배열을 String형태로 합치기
join()
// 조건에 맞는 새로운 배열을 만들기 ( 모든 배열의 요소 중에 길이가 5 이상인 요소로만 새로운 배열을 만드는 등 )
filter()
// 배열의 모든 요소를 원하는 방식으로 업데이트하여 새로운 배열을 만들기 ( 모든 배열의 요소에 +1 하는 등)
map()
// 배열의 모든 요소를 원하는 방식으로 줄여서 하나의 값만 남김 ( 모든 배열의 요소의 합을 리턴하는 등 )
reduce()
Array.splice()
이 메소드는 배열에서 특정 위치의 값을 원하는 내용으로 바꾸는 메소드이다. 특정 배열의 요소만 다른 값으로 대체해야하는 경우 사용할 수 있다.
let arr = [1, 2, '3', 4];
arr.splice(2, 1, 3);
console.log(arr);
// [1, 2, 3, 4]
splice( index to start the method, # of element to remove, the value to change )
splice( 바꾸고싶은 위치를 담은 index, 지우고 싶은 요소의 개수(index부터 1이다), 바꾸고 싶은 값(대체하고 싶은 값) )
이 로직을 이제 만들어보자!
내가 이해한 바로는 arr.slice(a, b, c)에서 파라미터가 하는 역할이 다르다!
a의 최대값은 arr.length이고, b의 최대값은 arr.length - a 이다. c는 [...c] 와 같이 옵셔널하게 넣어주면 된다. 그리고, a를 index로 이용하여 메소드가 적용될 구간을 나누고, 길이 b만큼을 없애자! 마지막에, c를 b자리에 넣고, arr에 남은 부분을 붙여주자!
// 실제 splice는 입력받은 arr 자체를 수정하고, 결과값으로 사라지는 배열의 부분을 리턴한다
// mySplice는 유사 splice이다
let mySplice = function (arr, a, b, ...c) {
if ( !Array.isArray(arr) ) {
return 'ERROR: First parameter must be an array.\n'
}
if ( typeof(a) !== 'number' ) {
return 'ERROR: Second parameter must be an index number of array.\n'
}
if ( b === undefined || typeof(b) !== 'number' ) {
b = 0;
}
if ( c === undefined ) {
c = '';
}
// a 이전의 요소를 앞 배열에 저장
let previousArr = arr.slice(0, a);
// a+b 이후 요소를 뒷 배열에 저장
let lastArr = arr.slice(a+b);
// c를 앞 배열의 뒤에 붙여줌
for ( let i of c ) {
previousArr.push(i);
}
// 앞 배열과 뒷 배열을 합쳐 리턴해줌
return previousArr.concat(lastArr);
}
let arr1 = [0, 1, 2, 3, 4, 5, 6]
let arr2 = [0, 1, 2, 3, 4, 5, 6]
arr1.splice(2, 2, 1);
console.log(arr1);
// [ 0, 1, 1, 4, 5, 6 ]
console.log(mySplice(arr2, 2, 2, 1));
// [ 0, 1, 1, 4, 5, 6 ]
Array.join()
이 메소드는 배열을 순서대로 연결하여 하나의 문자열로 만들어낸다. 괄호() 안에 배열의 사이에 어떤 것을 넣을 지 정할 수 있다.
let arr1 = [1, 2, 3, 4, 5];
let arr2 = ['a', 'b', 'c', 'd'];
let a1 = arr1.join(); // default는 ',' 입니다
let a2 = arr2.join();
console.log(a1); // '1,2,3,4,5'
console.log(a2); // 'a,b,c,d'
a1 = arr1.join('');
a2 = arr2.join('');
console.log(a1); // '12345'
console.log(a2); // 'abcd'
a1 = arr1.join(' ');
a2 = arr2.join(' ');
console.log(a1); // '1 2 3 4 5'
console.log(a2); // 'a b c d'
마찬가지로, Koans에서 Brain을 넣는 부분은 아래와 같다.
let noOfBrains = 4;
let mastermind = 'Brain';
let result = Array(noOfBrains + 1).join(' ' + mastermind);
console.log(Array(noOfBrains + 1));
// [ empty x 5 ] <-- 이거는 [ , , , , ] 이 말이다
console.log(result);
// ' Brain Brain Brain Brain'
이제 로직을 세워보자. join()은 배열의 요소를 '+ 연산'을 통해 순서대로 합쳐준다. 만약 괄호() 안에 아무것도 없다면 합칠 때 ','를 넣어주고, 무언가 넣어 주었다면 괄호 안의 내용(문자열)으로 사이를 메워준다.
// 배열을 문자열로 변환시켜준다. 사이에 원하는 값을 넣을 수 있다.
let myJoin = function (arr, value) {
if ( !Array.isArray(arr) ) {
return 'ERROR: First parameter must be an array.\n'
}
if ( value === undefined ) {
value = ',';
}
let result = '';
result += arr[0];
for ( let i = 1; i < arr.length; i++ ) {
result += `${value}${arr[i]}`;
}
return result;
}
let arr = [1, 2, 3, 4, 5, 6];
let result = myJoin(arr, ' ');
console.log(arr);
// [ 1, 2, 3, 4, 5, 6 ]
console.log(`'${result}'`);
// '1 2 3 4 5 6'
Array.filter()
이 메소드는 배열의 요소에서 괄호()안에 넣은 함수를 실행해 조건을 충족하는 요소만 순서대로 새로운 배열에 넣어 결과를 리턴한다.
filter()에서 괄호()안에 들어가는 함수는 true, false로 나누어질 수 있는 함수가 들어가야 한다.
함수를 파라미터로 받는 건 처음이기 때문에 일단 설명부터 하고, 로직 및 코딩을 해보자.
let arr = [1, 2, 3, 4];
let result = arr.filter(x=>x>=3);
console.log(arr); // [1, 2, 3, 4]
console.log(result); // [3, 4]
이 로직은 매우매우매우매우매우매우 간단하다. 파라미터로 넘어 온 배열의 모든 요소를 함수에 집어넣는다. 함수의 조건을 만족한 배열의 요소만 새로운 배열에 집어넣는다.
하지만 함수를 파라미터로 받아오는 게 잘 이해가 안되었다. 2시간동안 각종 검색을 통해 해결하였다!!! 이해가 안되는 경우 댓글을 달아주세요!
/**
* 함수로 인자를 받는 걸 이해하는 게 힘들었습니다.
* 스코프와 클로저 개념을 먼저 이해하셔야 합니다.
* 스코프와 클로저 개념은 다음 포스트를 확인해주세요.
*/
// 배열과 함수를 파라미터로 받습니다
let myFilter = function (arr, fn) {
if ( !Array.isArray(arr) ) {
return 'ERROR: First parameter must be an array.\n'
}
if ( typeof(fn) !== 'function' ) {
return 'ERROR: Second parameter must be a function.\n'
}
let result = [];
// 배열의 모든 요소를 함수에 검사합니다.
// 입력된 함수는 true, false를 반환합니다.
for ( let value of arr ) {
// value를 함수에 파라미터로 넘겨주고 true, false를 반환받습니다.
if ( fn(value) ) {
// fn(value)가 true인 경우 새로운 배열 result에 푸쉬합니다.
result.push(value);
}
}
// 새로운 배열 result를 리턴합니다.
return result;
}
// 파라미터로 넘겨준 함수에 대한 설명입니다.
// ' x => (x > 3) ' 이 함수는 아래의 함수와 동일합니다
function functionName ( parameter ) {
// parameter > 3 이 true면 true, false면 false를 리턴합니다
return parameter > 3;
}
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result1 = myFilter( arr, x => (x > 3) );
let result2 = myFilter( arr, functionName );
console.log(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(result1);
// [4, 5, 6, 7, 8, 9, 10]
console.log(result2);
// [4, 5, 6, 7, 8, 9, 10]
Array.map()
이 메소드는 배열의 모든 요소를 파라미터로 넘어온 함수에 다시 파라미터로 넘겨주고 함수를 실행해 새로운 배열로 돌려준다. 괄호() 안에는 함수하나의 요소에 대해 원하는 변경값을 리턴하는 함수를 넣어주면 된다.
let arr1 = [1, 2, 3, 4]
let arr2 = arr1.map((a)=>(a+1));
console.log(arr1); // [1, 2, 3, 4]
console.log(arr2); // [2, 3, 4, 5]
이제 로직을 세워보자.
괄호()안에 함수를 받아오면서 이것을 내부함수로 사용하는 것같다. 배열의 모든 요소를 함수안에 집어넣고, 결과를 리턴받아 해당 요소의 인덱스 자리에 결과를 넣어준다. 음... 할 수 있을 거 같은데?
확실한 건 이 메소드는 파라미터로 함수를 받아오고, 파라미터 함수를 클로져함수로써 사용한다.
그래도 다행인 것은 함수를 사용하는 방법은 filter()에서 한 번 해봤으니까!
/**
* 함수로 인자를 받는 걸 이해하는 게 힘들었습니다.
* 스코프와 클로저 개념을 먼저 이해하셔야 합니다.
* 스코프와 클로저 개념은 다음 포스트를 확인해주세요.
*/
// 배열과 함수를 파라미터로 받습니다
let myMap = function (arr, fn) {
if ( !Array.isArray(arr) ) {
return 'ERROR: First parameter must be an array.\n'
}
if ( typeof(fn) !== 'function' ) {
return 'ERROR: Second parameter must be a function.\n'
}
let result = [];
// 배열의 모든 요소를 함수에 검사합니다.
// 입력된 함수는 정의된 기능을 수행하고, 그 결과를 반환합니다.
for ( let value of arr ) {
// value를 함수에 파라미터로 넘겨주고 함수의 결과를 반환받아 새로운 배열 result에 푸쉬합니다.
result.push(fn(value));
}
// 새로운 배열 result를 리턴합니다.
return result;
}
// 파라미터로 넘겨준 함수에 대한 설명입니다.
// ' x => (x + 3) ' 이 함수는 아래의 함수와 동일합니다
function functionName ( parameter ) {
// parameter에 3을 더해 리턴합니다
return parameter + 3;
}
let arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result1 = myMap( arr, x => (x + 3) );
let result2 = myMap( arr, functionName );
console.log(arr);
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
console.log(result1);
// [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
console.log(result2);
// [4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
Array.reduce()
이 메소드는 배열의 모든 요소를 괄호()안의 내용에 따라 특정 기능을 수행하고 결과값으로 단 하나만 리턴한다.
이름이 reduce인 이유도 그거다! map()은 1:1 매칭인 데 반해, reduce는 하나만 남기고 다 무시한다! 결과를 하나로 줄이는 녀석인 것이다!
예를 들어, 배열의 합을 구하고자 하는 경우에는 아래와 같이 작성하면 끝이다.
let arr = [1, 2, 3, 4];
let sum = arr.reduce((a, c)=>(a+c), 0);
console.log(arr); // [1, 2, 3, 4]
console.log(sum); // 10
reduce()는 filter(조건비교함수)나 map(배열요소값변경)과 달리 reducer 함수를 넣어야한다.
MDN의 정의에는 'The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in single output value.'라고 되어있다.
번역해보면, reduce () 메소드는 배열의 각 요소에 대해 파라미터로 넣은 Reducer 함수를 실행하여 단일 출력 값을 생성합니다.
reduce()는 4개의 파라미터를 전달받을 수 있다.
// From MDN
arr.reduce(callback( accumulator, currentValue[, index[, array]] )[, initialValue])
// From Gray
reduce( accumulator, currentValue, initialValue)
/**
* accumulator는 초기값으로 시작하여 함수가 실행될 때마다의 결과를 저장한다
* currentValue는 배열을 돌며, 현재 검사중인 배열의 요소를 뜻한다
* for ( let value of array ) <<---- 에서 하나씩 돌아가는 value가 currentValue이다
* currentValue[, Index]는 옵션값이고, currentValue가 위치한 배열의 인덱스이다
* currentValue[, Index[, array]]는 옵션값이고, sourceArray 즉,배열이 호출됨을 말한다
* initialValue는 옵션값이지만, 넣어서 시작하길 권장한다.
* 넣어준 accumulator의 초기값에 initialValue가 할당된다
*
* 아래의 예시는 MDN에서 가져왔고, 주석은 새로 달았다
*
*/
// maxCallback은 reducer 함수로, acc.x와 cur.x 중 큰 값을 리턴한다
let maxCallback = ( acc, cur ) => Math.max( acc.x, cur.x );
// maxCallback2는 reducer 함수로, max와 cur 중 큰 값을 리턴한다
let maxCallback2 = ( max, cur ) => Math.max( max, cur );
// initialValue ( 초기값 )가 없는 상태로 리듀서함수 maxCallback을 돌린 경우
// acc: {x: 2}, cur: {x: 22}, return 22;
// acc: 22, cur: {x: 42}, return NaN; // 숫자와 오브젝트를 비교했기 때문에!
[ { x: 2 }, { x: 22 }, { x: 42 } ].reduce( maxCallback ); // NaN
// acc: {x: 2}, cur: {x: 22}, return 22; // 종료
[ { x: 2 }, { x: 22 } ].reduce( maxCallback ); // 22
// 요소가 하나 뿐이기때문에 reduce 작업이 실행되지 않고 currentValue를 그대로 리턴하고 종료
[ { x: 2 } ].reduce( maxCallback ); // { x: 2 }
// 빈 배열은 타입에러
[ ].reduce( maxCallback ); // TypeError
// map & reduce with initialValue; better solution, also works for empty or larger arrays
// map과 reduce를 사용할 때에는 initialValue를 적용하는 게 좋은 방법입니다.
// 빈 배열이나 큰 배열에도 좋습니다.
[ { x: 22 }, { x: 42 } ].map( el => el.x )
.reduce( maxCallback2, -Infinity );
/**
* 위 reduce( maxCallback2 ) 실행결과
* reducer에 acc와 cur을 넣을 때, 객체의 값이 아닌 키를 넣어주었습니다.
* map()을 이용해 넣어준 키를 이용해서, 모든 요소를 비교하고자하는 객체의 값으로 매핑하였습니다.
* reduce()를 돌립니다. 아래는 결과입니다.
* max: -Infinity, cur: 22, Math.max(): 22 // max는 위에서 입력해준 initialValue = -Infinity
* max: 22, cur: 42, Math.max(): 42
* 결과: 42
*/
reduce() 메소드를 조금 더 쉽게 이해하기 위해, 아래와 같이 돌려봅니다.
// 빈 배열을 넣을 때
let arr = [];
let reducer = function ( acc, cur ) {
console.log(`acc: ${acc}, cur: ${cur}, Math.max(): ${Math.max(acc, cur)}`);
return Math.max(acc, cur);
}
arr.reduce(reducer);
// 요소가 0개이기 때문에 타입 에러를 뿜습니다.
//Uncaught TypeError: Reduce of empty array with no initial value
// 배열의 요소가 1개일 때
let arr = [1];
let reducer = function ( acc, cur ) {
console.log(`acc: ${acc}, cur: ${cur}, Math.max(): ${Math.max(acc, cur)}`);
return Math.max(acc, cur);
}
arr.reduce(reducer);
// 요소가 1개이기 때문에 reducer를 수행하지 않고 리턴합니다.
// 1
let arr = [1, 2];
// reducer로 가장 큰 값을 찾아내는 함수(Math.max())를 넣었습니다.
let reducer = function ( acc, cur ) {
console.log(`acc: ${acc}, cur: ${cur}, Math.max(): ${Math.max(acc, cur)}`);
return Math.max(acc, cur);
}
arr.reduce(reducer);
// 비로소 돌아갑니다.
// acc: 1, cur: 2, Math.max(): 2
// 2
빈 배열의 경우 타입에러를 뿜어내고, 배열의 요소가 1개라면 해당 요소만 리턴합니다.
가장 아래에는 2개의 요소로 max값을 찾도록 했는데요! 2가 잘 리턴되는 것을 볼 수 있습니다.
조금 더 명확하게 reduce() 메소드를 사용하려면, 초기값 ( initialValue ) 를 넣어주세요! 아래는 배열의 요소가 1개일 때, initialValue가 어떤 역할을 하는 지 보여줍니다.
// 초기값을 넣어줍시다
let arr = [1];
// reducer로 가장 큰 값을 찾아내는 함수(Math.max())를 넣었습니다.
let reducer = function ( acc, cur ) {
console.log(`acc: ${acc}, cur: ${cur}, Math.max(): ${Math.max(acc, cur)}`);
return Math.max(acc, cur);
}
arr.reduce(reducer, 0);
// initialValue로 0을 넣어주었습니다.
// 이 경우 accumulator에 0이 할당된 상태로 currentValue와 함수를 실행합니다.
// acc: 0, cur: 1, Math.max(): 1
// 1
이제 로직을 세워보자.
reduce()는 괄호()안에 두 개의 파라미터를 받아온다. 리듀서 함수와 초기값이다. 리듀서 함수에는 두 개의 파라미터가 들어가는데, 각 파라미터는 accumulator ( 이전까지의 연산결과 ) , currentValue ( 현재값 ) 이다. 함수를 실행하고, 결과를 돌려주면 될 거 같다.
/**
* 함수로 인자를 받는 걸 이해하는 게 힘들었습니다.
* 스코프와 클로저 개념을 먼저 이해하셔야 합니다.
* 스코프와 클로저 개념은 다음 포스트를 확인해주세요.
* reduce method는 reducer 함수가 필요합니다.
*/
// 배열과 함수 reducer, 초기값을 파라미터로 받습니다
let myReduce = function (arr, fn, initialValue) {
if ( !Array.isArray(arr) ) {
return 'ERROR: First parameter must be an array.\n'
}
if ( typeof(fn) !== 'function' ) {
return 'ERROR: Second parameter must be a function.\n'
}
if ( initialValue === undefined ) {
initialValue = arr[0];
}
let result;
// 비어있는 accumulator에 initialValue;를 할당합니다.
let accumulator = initialValue;
// 배열의 모든 요소를 함수에 검사합니다.
// 입력된 리듀서함수는 정의된 기능을 수행하고, 그 결과를 반환합니다.
for ( let currentValue of arr ) {
// accumulator와 currentValue를 함수에 파라미터로 넘겨주고,
// 함수의 결과를 반환받아 변수 result에 할당합니다.
result = fn( accumulator, currentValue );
accumulator = fn( accumulator, currentValue );
}
// 결과 result를 리턴합니다.
return result;
}
// 파라미터로 넘겨준 함수에 대한 설명입니다.
// 함수 reducer1을 표현식으로 선언하였습니다.
let reducer1 = function ( acc, cur ) {
if ( acc < cur ) {
return cur;
}
return acc;
}
// 함수 reducer2를 표현식으로 선언하였습니다.
let reducer2 = ( acc, cur ) => ( acc + cur );
let arr1 = [1, 2, 3, 10, 5, 6, 7, 8, 9, 4];
let arr2 = [11, 21, 31, 110, 15, 61, 17, 18, 19, 41];
let result1 = myReduce( arr1, reducer1, 0 );
let result2 = myReduce( arr2, reducer2, 0 );
console.log(arr1);
// [1, 2, 3, 10, 5, 6, 7, 8, 9, 4]
console.log(result1);
// 10
console.log(arr2);
// [11, 21, 31, 110, 15, 61, 17, 18, 19, 41];
console.log(result2);
// 344
// reduce() 메소드는 배열의 모든 요소를 원하는 방식으로 줄여서 하나의 값만 남깁니다. ( 모든 배열의 요소의 합을 리턴하는 등 )
다음 포스트는 scope ( 스코프 )와 closure ( 클로저 ) 에 대한 포스팅입니다.
클래스까지 포스팅이 되고나면, 기존에 작성한 메소드를 변경해서 나만의 메소드 만들기를 진행합니다.
아래에 포함된 메소드는 정리돼있는 포스트를 확인하자
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (1)
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (1)
// 문자열의 알파벳을 전부 대문자로 바꾸거나 소문자로 바꾸기
String.toUpperCase / String.toLowerCase
// 문자열을 잘라 배열에 담기
String.split()
// 빈 배열 찾기
빈 배열 찾기
// 배열인지 확인하기
Array.isArray(arr)
// 배열 내부의 값 정렬하기
Array.sort([compare function))
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (2)
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (2)
// 배열의 요소를 추가하거나 삭제하는 메소드
Array.push() / Array.unshift() / Array.pop() / Array.shift()
// 두 개의 배열을 하나의 배열로 합치기
Array.concat()
// 배열 자르기
Array.slice()
(현재) [JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (3)
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (3)
// 배열의 요소를 대체하기
Array.splice()
// 배열을 String형태로 합치기
Array.join()
// 조건에 맞는 새로운 배열을 만들기 ( 모든 배열의 요소 중에 길이가 5 이상인 요소로만 새로운 배열을 만드는 등 )
Array.filter()
// 배열의 모든 요소를 원하는 방식으로 업데이트하여 새로운 배열을 만들기 ( 모든 배열의 요소에 +1 하는 등)
Array.map()
// 배열의 모든 요소를 원하는 방식으로 줄여서 하나의 값만 남김 ( 모든 배열의 요소의 합을 리턴하는 등 )
Array.reduce()
'JavaScript' 카테고리의 다른 글
[Javascript] 클래스의 요소와 메소드 (0) | 2020.08.15 |
---|---|
[JavaScript] Scope & Closure ( 스코프와 클로저 ) (0) | 2020.08.06 |
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (2) (0) | 2020.08.03 |
[JavaScript] 문자열과 배열, 그리고 객체에서 유용한 각종 Method (1) (0) | 2020.08.02 |
[JavaScript] 배열과 객체 (0) | 2020.07.30 |