관리 메뉴

흰둥씨의 개발장

[함수형 코딩#14] 중첩데이터에 함수형도구 사용 본문

함수형 프로그래밍/쏙쏙 들어오는 함수형 코딩

[함수형 코딩#14] 중첩데이터에 함수형도구 사용

돈워리비해삐 2023. 7. 12. 01:13

# 이번장의 핵심내용 : 객체를 다루는 함수형 도구 알아보기 

어떤객체라도 바꿀수 있는 함수update()를 만들어서 재활용성을 높이자

function objectSet(obj, key, value){  //카피온라이트
    let copy = Object.assign({}, obj);
    copy[key] = value;
    return copy;
}

function update(obj, key, modify){ //여러 함수들 중 객체상태를 업데이트 하는 동작이 많아 
                                   //재사용할수있도록 고차함수로 update()를 만듦
    let value= obj[key];
    let newValue = modify(value);
    let newObj = objectSet(obj, key, newValue);
    return newObj;
}

function update2(obj, key1, key2, modify){ //2depth로 중첩된 객체변경시 사용위함
    return update(obj, key1, (value)=>{	
    	return update(value, key2, modify);
        });
}

function update3(obj, key1, key2, key3, modify){//3depth로 중첩된 객체변경시 사용위함
	return update(obj, key1, (obj2)=>{
    	return update2(obj2, key2, key3, modify);
        });
}
//update()사용예시 
let em = {
    name :'kim',
    salary : 1200000
    }
function raise10 (salary){
	return salary * 1.1;
}
update(em, 'salary', raise10); //{name :'kim', salary : 1320000 }
//update2()사용해보기
let shirt = {
	name: 'shirt',
    price:13,
    options: {	
    	color: 'blue',
        size: 3, 
        }
    };
    
function incrementSize (item){
	return update2(item, 'options', 'size', (size)=>{
    	return size + 1;
        });
}

incrementSize(shirt);  //{name: 'shirt', price: 13, options: {color: 'blue', size: 4}}
//update3()사용해보기 
function incrementSizeByName(cart, name){
	return update3(cart, name, 'options', 'size', (size)=>{
    	return size + 1;
        })
}
let cart = {
	shirt : {
    	name: 'shirt',
        price : 13, 
        options : {
        	color:'blue',
            size:3
            }
        }
     }
incrementSizeByName(cart, 'shirt');
/*{shirt: {
      name: 'shirt', 
      price: 13, 
      options: {color: 'blue', size: 4}
      }
}*/

 

여기서 의문... 객체의 depths가 깊어질때마다 함수를 만들어야 할까...? 얼마만큼의 depths도 대응 될수 있도록 함수를 만들자 
=> 재귀 함수로 작성 해보기(재귀 함수는 "자신이 불렀던 곳이 어디인지를 유지하기 위해" stack을 사용함)

function drop_first(arr){  //카피온라이트 
    let arrCopy = arr.slice();
    arrCopy.shift();
    return arrCopy;
}

function nestedUpdate (obj, keys, modify){  //재귀호출을 해서 어떤 depths의 객체도 변경할 수 있음
	if(keys.length === 0){ 
    	return modify(obj);
        }
    let key1 = keys[0];
    let restOfKeys = drop_first(keys);
    return update(obj, key1, (value) => {
    	return nestedUpdate(value, restOfKeys, modify); //재귀 호출 
        });
}
//nestedUpdate()사용해보기 => 4depths 객체 변경하기 

function incrementSizeByName(cart, name){
	return nestedUpdate(cart, [name, 'shirt', 'options', 'size'], (size)=>{
    	return size + 1;
        })
}
let cart = {
 clothing:{
	shirt : {
    	name: 'shirt',
        price : 13, 
        options : {
        	color:'blue',
            size:3
            }
        }
     }
}
incrementSizeByName(cart, 'clothing');
/*{clothing: {
    shirt: {
      name: 'shirt', 
      price: 13, 
      options: {color: 'blue', size: 4}
      }
    }
}*/

 

incrementSizeByName()을 보면 depth가 깊을 수록 직접 객체의 키 경로를 정확히 알고 변경해줘야 함
ㄴ> 이럴 때 추상화의 벽에 내가 받아온 데이터가 뭔지(+그 데이터들을 각각 조작하는 동작)를 리턴해주는 함수를 만들어주면 좋음