Search code examples
javascripttypescriptfunctional-programmingpurely-functional

Functional Programming: How to convert an impure function to a pure function when the input needs to be mutated


How can I create a pure function that updates an object that's been initialized in another function something like:

parentFunction = (inputs: object[], condtionList: string[]) => {

  const newObject = {f1: val1[], f2: val2[], f3: val3[]...}
  inputs.forEach(input => {
    if(condition1){
      updateNewObject(condtion1, newObject, input, conditionList)
    }
    .
    .
    . 
  }
  return newObject
}

The below function is impure as it's updating the newObject (mutating the input) how can I convert it to a pure function?

updateNewObject(condition, newObject, input, conditionList) {
  const i = conditionList.indexOf(input.condition)
  if(i === 0){
    newObject.f1.push(input)
  }
  else if(i === 1) {
    newObject.f2.push(input)
  }
  .
  .
  .
}

The above has no return value. It takes the newObject as input and based on some conditionals pushes values to the properties of the newObject. Is there anyway to make the above function pure? or do I have to rethink how I am updating newObject?


Solution

  • If you want updateNewObject to be pure, have it create a new object that clones the original, mutate that, and then return the new object.

    updateNewObject(condition, oldObject, input, conditionList) {
      const newObject = {...oldObject};
      const i = conditionList.indexOf(input.condition)
      if(i === 0){
        newObject.f1 = [...newObject.f1, input];
      }
      else if(i === 1) {
        newObject.f2 = [...newObject.f2, input];
      }
      .
      .
      .
    
      return newObject;
    }
    

    Note how newObject.f1 = [...newObject.f1, input]; creates a new array - this ensures that we not only don't mutate the object directly, but we don't mutate any of its fields (arrays) and instead create new ones.

    Then tweak parentFunction so that it uses the value of each returned updateNewObject call:

    parentFunction = (inputs: object[], condtionList: string[]) => {
    
      let newObject = {f1: val1[], f2: val2[], f3: val3[]...}
      inputs.forEach(input => {
        if(condition1){
          newObject = updateNewObject(condtion1, newObject, input, conditionList)
        }
        .
        .
        . 
      }
      return newObject
    }