/* 요소 리스트 값을 하나씩 꺼내어 준다.*/
function each(list, iter){
const _keys = keys(list);
for(let i=0;i<_keys.length;i++){
iter(list[_keys[i]], _keys[i]);//값 뿐만이 아니라 키도 인자로 넘기도록 수정
}
return list;
}
/* 수집하기 대표 요소를 변환시키는 함수*/
function map(list , mapper){
const result = [];
each(list, (val,key) => result.push(mapper(val,key)));//키도 넘김
return result;
}
pairs
key:value 쌍으로하는 배열을 반환하는 함수
/*key:value 쌍으로하는 배열을 반환하는 함수*/
function pairs (list){
return map(list, (val, key)=>[key,val]);
}
console.log(
_.pairs(users[0]),
)
_.go(
users,
_.map(u=>_.pairs(u)),
console.log
)
마무리
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script>
//순수함수 예시
function add(a,b){
return a+b;
}
console.log(add(12,34));
console.log(add(12,34));
//부수효과 예시
let sideEffect1 = 10;
function add2(a,b){
return add(a,b)+ sideEffect1;
}
console.log(add2(10,20));
sideEffect1 = 20;
console.log(add2(10,20));
let sideEffect2 = 10;
function add3(a,b){
sideEffect1 = a; // 외부에 영향을 준다
sideEffect2 = b;
return add(a,b);
}
console.log(add3(10,20));
console.log(add3(10,20));
//새로운 값을 만든다는 것은 Call By Reference가 대상이다.
const arr = [1,2,3,4,5];
function multiplyArr(arr){
//새로운 값
const result = arr.slice();
for(const idx in result){
result[idx] = result[idx] * 2;
}
return result;
}
console.log(multiplyArr(arr));
console.log(arr);
console.clear();
//일급함수
const get10 = () => 10; //함수를 변수에 담는다.
//함수를 인자로 받는 함수, 함수를 변수에 담았음
const add30 = function(fn){
return 30 + fn();
}
//인자로 함수를 전달했다.
console.log(add30(get10));
//함수는 단독으로 존재할 수 있다.
function hello(name){
console.log("hello "+name);
}
//메서드는 클래스 속에 존재한다.
class Hello{
hello(name){
console.log("hello "+name);
}
}
hello("홍길동");
new Hello().hello("홍길동");
console.clear();
</script>
<script>
const users = [
{ id: 10, name: 'ID', age: 36 },
{ id: 20, name: 'BJ', age: 32 },
{ id: 30, name: 'JM', age: 32 },
{ id: 40, name: 'PJ', age: 27 },
{ id: 50, name: 'HA', age: 25 },
{ id: 60, name: 'JE', age: 26 },
{ id: 70, name: 'JI', age: 31 },
{ id: 80, name: 'MP', age: 23 }
];
//함수를 담은 obj
const _ = (()=>{
return {
filter:curryr(filter) ,
reject:curryr(reject) ,
negate:negate ,
compact:compact ,
find : find,
find_index : find_index,
some : some,
every : every,
contains : contains,
map:curryr(map) ,
get:curryr(get) ,
values:values,
pluck:pluck,
pairs:pairs,
identity:identity,
each: each,
curry:curry,
curryr:curryr,
reduce: curryr(reduce),
min: min,
max: max,
min_by: curryr(min_by),
max_by: curryr(max_by),
group_by: curryr(group_by),
count_by: curryr(count_by),
rest: rest,
pipe: pipe,
go: go,
};
/*찾아내기 대표 함수 find
filter와 유사하나 predi에 걸러진 처음으로 만나는 요소를 반환한다.*/
function find(list, predi){
const _keys = keys(list);
for(let i=0;i<_keys.length;i++){
const val = list[_keys[i]];
if(predi(val)) return val;
}
return undefined; //생략 가능, 자동으로 undefined 리턴될 것
}
/*predi에 걸러진 처음으로 만나는 요소의 인덱스를 리턴한다.*/
function find_index(list, predi){
const _keys = keys(list);
for(let i=0;i<_keys.length;i++){
if(predi(list[_keys[i]])) return i;
}
return -1; //자바스크립트 표준에 맞춤
}
/* find_index 특화 함수 주어진 조건에 값이 하나라도 존재하는지 검사한다.*/
function some(list, predi){
//하나라도 해당하면 -1을 리턴할 수 없다.
return find_index(list, predi||identity) !== -1;
}
/* find_index 특화 함수 주어진 조건에 값이 전부 일치하는지 검사한다.*/
function every(list, predi){
//주어진 조건 반대에 해당하는 값을 못찾으면, 전부 해당하는 것
return find_index(list, negate(predi||identity)) === -1;
}
function every2(list, predi){
return find_index(list, function(val){
return !predi(val);
})
}
function every3(list, predi){
return find_index(list, (val)=> !predi(val) )
}
/*주어진 값이 존재하는지 검사하는 함수*/
function contains(list, data){
return find_index(list, val => val === data) !== -1;
}
/* 요소를 걸러내는 함수, predi라는 보조함수를 인자로 받는다. */
function filter(list, predi){
//함수형 스타일은 기존 요소를 손상시키지 않고 항상 새로운 값을 리턴한다.
const result = [];
//보조함수를 인자로 받아
each(list, val =>{
if(predi(val)) result.push(val);
});
return result;
}
/*단순히 filter에 반대로 행동하는 함수
(== 주어진 조건에 해당하는 값을 반환하는 함수*/
function reject(list, predi){
return filter(list, negate(predi) );
}
/*결과를 반대로 바꾸는 함수*/
function negate(predi){
return val => !predi(val);
}
/*참으로 판단되는 값을 걸러내는 함수*/
function compact(list){
return filter(list, identity);
}
/* 수집하기 대표 요소를 변환시키는 함수*/
function map(list , mapper){
const result = [];
each(list, (val,key) => result.push(mapper(val,key)));
return result;
}
/*key:value 쌍으로하는 배열을 반환하는 함수*/
function pairs (list){
return map(list, (val, key)=>[key,val]);
}
/*요소 값을 반환하는 함수*/
function values(list){
return map(list,identity);
}
/*자기자신을 그대로 반환하는 함수*/
function identity(arg){
return arg;
}
/*요소에서 특정 값만 추출하는 함수 */
function pluck(list, key){
return map(list, obj => obj[key]);
}
/* 요소 리스트 값을 하나씩 꺼내어 준다.*/
function each(list, iter){
const _keys = keys(list);
for(let i=0;i<_keys.length;i++){
iter(list[_keys[i]], _keys[i]);
}
return list;
}
/*안전하게 값 가져오기*/
function get (obj,key){
return obj === null ? undefined : obj[key];
}
/* 객체의 keys를 안전하게 리턴하는 함수*/
function keys(obj){
return is_object(obj) ? Object.keys(obj) : [];
}
/* 객체인지 안전하게 확인하는 함수 */
function is_object(obj){
return typeof obj === "object" && !!obj;
}
/* 커링 함수 오직 함수만을 인자로 받아 함수를 리턴함, 평가 시점을 조율*/
function curry(fn){
return function(a,b){
return arguments.length === 2 ? fn(a,b) : function (b){
return fn(a,b);
}
}
}
/* 단순히 인자 순서만 변경, 화살표 함수로만 표현 */
function curryr(fn){
return function(a,b,c){
if(arguments.length === 2) return fn(a,b);
else if (arguments.length === 3) return fn(a,b,c);
else return b => fn(b,a);
};
}
/*요소를 하나로 접어가는 함수
list 요소, iter 보조함수, memo 초기값*/
function reduce(list, iter, memo){
if(arguments.length===2){
memo = list[0];
list = rest(list);
}
each(list, val =>{
//memo에 계속 누적하여 적용한다.
memo = iter(memo, val);
});
return memo;
}
function min(list){
return reduce(list, (a, b)=> a > b ? b : a);
}
function max(list){
return reduce(list, (a, b)=> a < b ? b : a);
}
/*보조 함수를 인자로 받아, 그 반환값을 기준으로 평가한다.*/
function min_by(list,iter){
return reduce(list, (a, b)=> iter(a) > iter(b) ? b : a);
}
function max_by(list,iter){
return reduce(list, (a, b)=> iter(a) < iter(b) ? b : a);
}
/*주어진 조건으로 그룹핑한 결과를 반환하는 함수*/
function group_by(list,iter){
return reduce(list, (group, val) => {
//그룹화하려면 그룹 기준과 그룹화될 리스트 공간이 필요하다.
//그룹 기준에 충족하는 첫 그룹화라면 빈 배열을 넣어준다.
(group[iter(val)] = group[iter(val)] || [] ).push(val);
return group;
} ,{});
}
/*주어진 조건으로 그룹핑한 결과를 개수로 반환하는 함수*/
function count_by(list,iter){
return reduce(list, (group, val) => {
group[iter(val)] = group[iter(val)]+1 || 1;
return group;
} ,{});
}
/* 배열 첫 번째 요소를 잘라 나머지 배열을 리턴*/
function rest(list, num){
return Array.prototype.slice.call(list,num||1);
}
/*리듀스 특화 함수, 함수만을 인자로 받는 함수*/
function pipe(){
const fns = arguments;
return function(val){
return reduce(fns,function(val,fn){
return fn(val);
}, val );
};
}
/*pipe 즉시 실행 버전*/
function go(data){
//첫번째 데이터만 제외하면 함수 배열이다.
const fns = rest(arguments);
//함수 배열이기 때문에 arg1, arg2 ... argN 풀어주려면 apply를 사용한ㄷ.
return pipe.apply(null,fns)(data);
}
})();
//나이 25세 이상 사용자 걸러내기
//절차지향 코드
const tmp = [];
for(const user of users){
if(user.age>=25) tmp.push(user);
}
console.log(tmp);
//함수형 코드
console.log(
_.filter(users, u=>u.age>=25)
);
console.log(
_.filter(users, u=>u.age-(u.age%10) === 20), //20대만
_.filter(users, u=>u.name.startsWith("J")) //J로 시작하는 user
);
//map 사용
console.log(
_.map(users, u=>u.age) ,
_.map(users, u=>u.name) ,
_.map(users, u=>u.id) ,
);
_.each(users,u=>console.log(u));
//섞어쓰기, 30세 이상, user 이름
console.log(
_.map(_.filter(users, u=> u.age >=30), u=>u.name)
);
console.clear();
//중복이름으로 구분자 _
const _add = _.curryr((a,b)=>a+b);
const _add10 = _add(10);
console.log(_add);
console.log(_add10);
console.log(_add10(20));
console.log(_add(10,20));
const _sub = _.curryr((a,b)=>a-b);
const _sub10 = _sub(10);
console.log( _sub10(20) );
console.clear();
console.log(_.filter);
console.log(_.map);
const upperAge30 = _.filter(u=>u.age>=30);
const userName = _.map(u=>u.name);
console.log(upperAge30);
console.log(userName);
console.log(upperAge30(users));
console.log(userName(users));
console.log(userName(upperAge30(users)));
console.clear();
/*이전 reduce 실습 내용*/
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,0));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,10));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val,-10));
console.log(_.reduce([1,2,3,4,5],(sum,val)=>sum+val ));
//30세 이상 사용자 age 합
console.log(_.reduce(_.map(upperAge30(users),u=>u.age),(sum,val)=>sum+val ));
console.clear();
//30세 이상 사용자 age 합, pipe 사용
const tmpFilter = _.filter(u=>u.age>=30);
const tmpMap = _.map(u=>u.age);
const tmpReduce = _.reduce((sum,val)=>sum+val);
console.log(tmpFilter);
console.log(tmpMap);
console.log(tmpReduce);
console.clear();
_.pipe(
tmpFilter,
tmpMap,
tmpReduce,
console.log
)(users);
_.go(users,
tmpFilter,
tmpMap,
tmpReduce,
console.log
);
_.each([1,2,3,4], n=> console.log(n))
_.each(null, n=> console.log(n))
console.log( _.filter(null, n=> true));
console.log( _.map(null, n=> n));
console.clear();
_.each({a:1,b:2,c:3}, a=>console.log(a));
_.each([1,2,3], a=>console.log(a));
//Object.keys 동작방식
console.log(Object.keys(["a","b","c"]).join("-") );
console.log(Object.keys("가나다라").join("-") );
console.log(Object.keys({고:"바",구:"바",마:"나"}).join("-") );
//Object.values 동일
console.log(Object.values(["a","b","c"]).join("-") );
console.log(Object.values("가나다라").join("-") );
console.log(Object.values({고:"바",구:"바",마:"나"}).join("-") );
//Object.keys 는 for...in 과 유사
function forIn(list){
const result = [];
for(const arg in list) result.push(arg);
return result.join("-");
}
//Object.values 는 for...of 과 유사
function forOf(list){
const result = [];
for(const arg of list) result.push(arg);
return result.join("-");
}
console.log(forIn(["a","b","c"]) );
console.log(forIn("가나다라") );
console.log(forIn({고:"바",구:"바",마:"나"}));
console.log(forOf(["a","b","c"]) );
console.log(forOf("가나다라") );
// console.log(forOf({고:"바",구:"바",마:"나"}));//불가능
//단일 접근 원칙
class Data{
constructor(name, age){
this._name = name;
this._age =age;
}
get name(){
console.log("name 게터 호출");
return this._name;
}
get age(){
console.log("age 게터 호출");
return this._age;
}
set name(arg){
console.log("name 세터 호출");
this._name = arg;
}
set age(arg){
console.log("age 세터 호출");
this._age = arg;
}
}
console.clear();
const data1 = new Data("홍길동", 50);
console.log(data1.name); //변수처럼 다룬다
data1.name = "임꺽정"
console.log(data1.name); //변수처럼 다룬다
console.clear();
console.log(
_.values(users),
_.values(users[0]),
);
console.log(
_.pluck(users, "age"),
_.pluck(users, "name"),
);
console.clear();
const filterEx = user=>user.age>30;
function ex(user){
return filterEx;
}
console.log(
_.filter(users, user=>user.age>30),
_.reject(users, user=>user.age>30),
)
const testArr = [undefined, Infinity, null, NaN, 0, 1, 'true','false',true,false];
console.log(
_.filter(_.identity)(testArr),
_.filter(_.negate(_.identity))(testArr),
);
const compactEx = _.filter(_.identity);
console.log(
compactEx(testArr),//이것과 완전히 같다.
_.compact(testArr)
)
/*
compactEx
b => fn(b,a) b는 _.filter , a는 _.identity , b로 testArr
_.compact
function compact(list){
return filter(list, identity);
}
화살표 함수로 바꾸면
list => filter(list, identity)
기호만 바꾸면
b => fn(b,a)
*/
console.clear();
console.log(
_.find(users, user => user.age >= 30),
_.filter(users, user => user.age >= 30),
);
console.log(
_.find_index(users, user => user.age <= 25),
_.find_index(users, user => user.age > 40),
);
console.clear();
console.log(
_.some(users, user=>user.age>30),
_.every(users, user=>user.age>30),
_.every(users, user=>user.age>10),
);
console.log(
_.some(users),
_.every(users),
_.some(testArr),
_.every(testArr),
);
console.log(
_.contains(users, users[2] ),
_.contains(users[2], "JM" ),
_.contains(users[2], "홍길동" ),
);
console.clear();
const numArr = [-10,10,20,30,-20,-50,66];
console.log(_.min(numArr));
console.log(_.max(numArr));
console.log(_.min_by(users, user=>user.age));
console.log(_.max_by(users, _.get("age")));
console.clear();
_.go(
users,
_.group_by(user=>user.age-(user.age%10)),
console.log
);
console.log(_.group_by(users,u=>u.age-(u.age%10)));
_.go(
users,
_.group_by(user=>user.age%2),
console.log
);
_.go(
users,
_.count_by(user=>user.age-(user.age%10)),
console.log
);
console.log(_.count_by(users,u=>u.age-(u.age%10)));
_.go(
users,
_.count_by(user=>user.age%2),
console.log
);
console.clear();
console.log(
_.pairs(users[0]),
)
_.go(
users,
_.map(u=>_.pairs(u)),
console.log
)
</script>
</body>
</html>
'개발 > 함수형 프로그래밍' 카테고리의 다른 글
자바스크립트 함수형 코드 자바화 해보기 - reduce (0) | 2023.06.22 |
---|---|
자바스크립트 함수형 코드 자바화 해보기 - filter, map, each (0) | 2023.06.21 |
10 - 컬렉션 중심 프로그래밍 - reduce(접기) (0) | 2023.06.19 |
09 - 컬렉션 중심 프로그래밍 - find(찾기) (0) | 2023.06.18 |
08 - 컬렉션 중심 프로그래밍 - filter(거르기) (0) | 2023.06.18 |