[map] → values, pluck
[filter] → reject, compact, without
[find] → some, every
[reduce] → min, max, group_by, count_by
map, filter, find, reduce
와 같은 고차 함수를 가지고
다른 헬퍼 함수들을 만들어 내는 것이 목표이다
기본 코드
function _each(list, iter) {
var keys = _keys(list);
for (let i=0, len = keys.length; i < len; i++) {
iter(list[keys[i]]);
}
}
function _map(list, mapper) {
const new_list = [];
_each(list, (val) => new_list.push(mapper(val)));
return new_list;
}
_values
: 값만 추출하기기본적인 values 함수 만들기
function _values(data) {
return _map(data, (val) => return val);
}
_identity
: 그대로 전달인자 값을 그대로 전달해주는
_identity
생성
map
에서 사용하는 mapper 함수
의function _identity(val) {
return val;
}
// 기본적 사용법
const a = 10;
console.log(_identity(a));
// values에 적용하기
function _values(data) {
return _map(data, _identity);
}
curryr을 이용한 리펙토링
curryr
의 성격인 인자가 하나만 들어오면
함수를 return
하도록 설정 하였기 때문에var _map = curryr(_map);
var values = _map(_identity); // 데이터를 받을 준비를 함
console.log(_map(_identity)(users[0]));
_pluck
: 원하는 키로 데이터만 추출key를 통해
function _pluck(data, key) {
return _map(data, function (obj) {
return obj[key];
});
}
const _pluck => (data, key) => {
return _map(data, (obj) => obj[key]);
}
_pluck(users, 'age');
_pluck
의 map 두번째 인자
로_get
함수를 사용할 수 있음// get
var _get = _curryr(function (obj, key) {
return obj == null ? undefined : obj[key];
});
// pluck
function _pluck(data, key) {
return _map(data, _get(key));
}
filter
를 반대로 동작
시키는 개념filter
: true 평가 항목을 제외
하는 것function _filter(list, predi) {
var new_list = [];
_each(list, function (val) {
if (predi(val)) new_list.push(val);
});
return new_list;
}
_reject
- filter 반대 동작_filter
에서 하던 일을반대로 동작
만 하도록구현
을 해주면 됨function _reject(data, predi) {
return _filter(data, function (val) {
return !predi(val);
});
}
_negate
: 함수를 반대로 return_reject
함수를 추상화 한 함수
함수를 return
하는 것이 특징조건의 반대 값
만 가져오는 함수function _negate(func) {
return function (val) {
return !func(val);
};
}
// _reject에 사용법
function _reject(data, predi) {
return _filter(data, _negate(predi));
}
_compact
: truthy 값만 남도록truthy
한 값만 남기게 됨
_identity를 이용
해서
┣ _filter
함수에 조건
을 넘겨주고
┣ 그 조건이 truthy
하다면
┗ 이를 반환하는 함수
를 구현
// _compact 함수 구현
function _identity(val) {
return val;
}
var _compact = _filter(_identity);
console.log(_compact([1, 2, 0, false, null, {}]));
predicate가 처음으로 true로 평가
되는값 하나를 return
하는 함수임function _is_object(obj) {
return typeof obj === 'object' && !!obj;
}
function _keys(obj) {
return _is_object(obj) ? Object.keys(obj) : [];
}
function _find(list, predi) {
var keys = _keys(list);
for (var i = 0, len = keys.length; i < len; i++) {
var val = list[keys[i]]; // 평가 조건과 반환값이 같기 때문에
// 두 번 사용되는 값을 변수에 담아버림
if (predi(val)) return val;
}
}
console.log(
_find(users, function (user) {
return user.age < 30;
})
);
predicate 함수
가 true 한 값
을 return 하는 경우지연 평가의 최적화 중 하나
find : 지연 평가의 최적화
_find_index
: 인덱스 값 찾기function _find_index(list, predi) {
var keys = _keys(list);
for (var i = 0, len = keys.length; i < len; i++) {
if (predi(list[keys[i]])) return i;
}
return -1; // JS의 기본 찾지 못하면
// -1을 return 하도록 설정함
}
console.log(
_get(
_find(user, function (user) {
return user.age === 30;
}),
'name'
)
);
_go
함수를 이용한 연속 동작_go(
find((user) => user.id === 50),
_get('name'),
console.log
);
_find
에 curryr을 사용var _find = _curryr(function (list, predi) {
var keys = _keys(list);
for (var i = 0, len = keys.length; i < len; i++) {
var val = list[keys[i]];
if (val) return val;
}
});
_some
: 조건 true 반환조건에 만족하는 값
이 하나라도
┗ 존재하면 true
를 반환함
_find_index
를 이용해서
┣ 조건에 만족하는지 → return이 -1이 아닌지
┗ 체킹하여서 이를 이용하게 됨
function _some(data, predi) {
return _find_index(data, predi) != -1;
}
모든 값이 조건에 만족
하면 : true
┗ 아니면 false를 반환함
_negate 함수를 이용
function _negate(func) {
return function (val) {
return !func(val);
};
}
// predi 함수의 반대 조건으로 탐색
// -1을 return 하게 되면 모든 조건이 참임을 의미함
function _every(data, predi) {
return _find_index(data, _negate(predi)) == -1;
}
predicate가 없으면
제대로 동작하지 않음
function _some(data, predi) {
return _find_index(data, predi || _identity) != -1;
}
function _every(data, predi) {
return _find_index(data, _negate(predi || _identity)) != -1;
}
이용법
아래와 같이 사용이 가능함
console.log(
_some(users, function (user) {
user.age == 20;
})
);
로직을 조합해 나가는 식으로 프로그램을 구성
순차적으로 사용하는 것이 아님
┣ 평가 순서와 상관없이
┣ 접고 펼쳐 나가는 것
이 특징
임
┣ 접기
혹은 축약
이라고 생각하면 됨
┣ 전혀 다른 값
을 만들 수 있음
Array
, iterable
한 객체
에 대해서
┣ 접혀진 값을 위해서 사용하게됨
function _reduce(list, iter, memo) {
if (arguments.length === 2) {
memo = list[0];
list = _rest(list);
}
_each(list, function (val) {
memo = iter(memo, val);
});
return memo;
}
_min
: 최소값find와 차이점
reduce의 경우
┣ 모든 값을 다 탐색함
reduce를 이용해서 함수
를 만들 때는조금 다른 방법으로 생각
하는 것이 중요평가 순서와 상관 없이 값
을 만드는 것을[1, 2, 4]
앞에서 부터 순서가 아닌순서와 상관 없이 해주는 것이 중요
두개의 값만 존재
한다고 생각function _min(data) {
return _reduce(data, function (a, b) {
return a < b ? a : b;
});
}
console.log(_min([1, 2, -4, 5, 10]));
function _max(data) {
return _reduce(data, function (a, b) {
return a > b ? a : b;
});
}
console.log(_min([1, 2, -4, 5, 10]));
_min_by
만들어보기min
에서 좀 더 확장성
을 길러줌(iter)
를 통해원하는 동작을 하면서 min, max를 구현이 가능함
함수를 넘겨주어서 조건을 설정이 가능함
기존 값을 변경하지 않는것이
┣ _min_by, _max_by 특징임
function _min_by(data, iter) {
return _reduce(data, function (a, b) {
return iter(a) < iter(b) ? a : b;
});
}
function _max_by(data, iter) {
return _reduce(data, function (a, b) {
return iter(a) > iter(b) ? a : b;
});
}
원하는 값이 아닌 다른 값이 출력 되게 됨
[1, 2, 3, -10] → 10
_map
이용[1, 2, 3, -10] → -10
_max_by
이용실용적인 예
user 중에 가장 나이가 많은 사람
을출력 하게 하는 예제
를 만든다면// 가장 나이가 어린 사람을 출력
console.log(
_min_by(users, function (user) {
return user.age;
})
);
_curryr
을 이용한 파이프라인_go
를 이용한 파이프라인 연속적 실행var _min_by = _curryr(_min_by),
_max_by = _curryr(_max_by);
_go(
users,
_filter((user) => user.age > 30),
_min_by((user) => user.age),
console.log
);
// 함수형으로 리펙터링
_go(
users,
_filter((user) => user.age >= 30),
_min_by(_get('age')),
_get('age'),
console.log
);
_go(
users,
_reject((user) => user.age >= 30),
_max_by(_get('age')),
_get('age'),
console.log
);
// max_by도 사용 가능
// reject 사용
// 이유 : reject : filter의 반대형
_go(
users,
_reject((user) => user.age > 30),
_max_by(get('user')),
// name을 다시 받아올 수 있음
_get('name'),
console.log
);
그룹
을 만드는 것이 이 함수의 목적
분류
를 한다고 생각하면 됨var _group_by = _curryr(function (data, iter) {
return _reduce(
data,
function (grouped, val) {
// 분류를 iter 함수에 위임
// key를 만들어서 grouped에 넣음
var key = iter(val);
(grouped[key] = grouped[key] || []).push(val);
return grouped;
},
{}
);
});
_group_by(users, (user) => user.age);
_push
: 안전성 높히기key값이 있고 없고에 대한
더 간단하게 표현
이var _push = function (obj, key, val) {
(obj[key] = obj[key] || []).push(val);
return obj;
};
// 이를 이용해서 _group_by 리펙터링
// 아래와 같이 간단하게 3줄로 표현이 가능함
var _group_by = _curryr(function (data, iter) {
return _reduce(data, function (grouped, val) {
return _push(grouped, iter(val), val);
});
});
curryr의 사용 이유
_go 함수에 사용하기 위함
┣ 함수를 넣으면 데이터가 나오기 때문에
┣ 쉽게 사용이 가능함
사용법
_go(
users,
_group_by((user) => user.age - user.age % 10)),
console.log
)
_head
함수 : 첫 인자 받아오기var _head = function (list) {
return list[0];
};
// 이용법
// 방법이 좋은 것은 아니지만
// 이러한 사용법도 있다는 것을 숙지
_go(users, _group_by(_pipe(_get('name'), _head)));
_go(
users,
_group_by((user) => user.name[0]),
console.log
);
_count_by
: key로 숫자세기reduce
사용할 때 주의점
for문을 머리속에서 지우는 것을 생각
순서대로 for문을 돌기 보다는
어떤 값을 return 하는지
에 대한 생각이 필요var _count_by = curryr(function (data, iter) {
return _reduce(
data,
function (count, val) {
var key = iter(val);
count[key] ? count[key]++ : (count[key] = 1);
return count;
},
{}
);
});
_inc
_push
함수와 동일함var _inc = function (count, key) {
count[key] ? count[key]++ : (count[key] = 1);
return count;
};
_each
, _map
다형성 높히기key값
을 같이 넘겨주게 됨const _each = _curryr(function (list, iter) {
const keys = _keys(list);
for (let i = 0, len = keys.length; i < len; i++) {
iter(list[keys[i]], keys[i]);
}
return list;
});
const _map = _curryr(function (list, mapper) {
const new_list = [];
_each(list, function (val, key) {
new_list.push(mapper(val, key));
});
return new_list;
});
_pairs
: key 값 찾아오기const _pairs = _map((key, val) => [key, val]);
컬렉션 중심 프로그래밍
을 사용하면테스트
가 쉬워지고안정성이 높아지고
직관적으로 이해
가 가능함