1

Similar to what lenses can do in functional languages, is there a way in JavaScript to return a new object identical to the original, but with some elements having been modified?

var myObject = {
  label: 'Table',
  options: ['legs'],
  params: {
    colour: 'red',
    feet: {
      colour: 'white',
      shape: 'round'
    }
  }
}

function newObject(obj) {
  // sought-after syntax here
  return obj({ params.colour = 'green', params.feet.shape = 'square' })
}

console.log(newObject(myObject))
{
  label: 'Table',
  options: ['legs'],
  params: {
    colour: 'green',
    feet: {
      colour: 'white',
      shape: 'square'
    }
  }
}

Note: newObject() returns a new object, without having affected the original in any shape or form.

Jivan
  • 21,522
  • 15
  • 80
  • 131
  • Is it a plain object or is it going to be complex? Also, does it have any functions assigned to it or is it just primitives, arrays, and other objects? – VLAZ Mar 03 '19 at 14:55
  • There is no direct support for that in JavaScript but it would of course be possible to implement code to do things like that. – Pointy Mar 03 '19 at 14:56
  • That would be an object composed only of primitives, arrays and other objects – Jivan Mar 03 '19 at 14:56
  • 1
    check immer or ramda or immutable – marzelin Mar 03 '19 at 15:02
  • Possible duplicate of [What is destructuring assignment and its uses?](https://stackoverflow.com/questions/54605286/what-is-destructuring-assignment-and-its-uses) – Code Maniac Mar 03 '19 at 15:04
  • 1
    JavaScript is functional enough that you can write your own lenses – Bergi Mar 03 '19 at 15:06

3 Answers3

2

You can try using Spread Syntax

var myObject = {
  label: 'Table',
  options: ['legs'],
  params: {
    colour: 'red',
    feet: {
      colour: 'white',
      shape: 'round'
    }
  }
}

function newObject(obj) {
  // sought-after syntax here
return ({...obj,params:{...obj.params,colour:"green",feet: {...obj.params.feet,shape:"square"}}})
}

console.log(newObject(myObject))
Maheer Ali
  • 35,834
  • 5
  • 42
  • 73
  • +1 for use of spread syntax. As a side note, there is a warning on this approach when using a lot of arguments, which may reach Javascript engine limit (which looks like depending of implementation, so from about 10 000 to 65 535 arguments). See [Spread_syntax#Spread_with_many_values](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_with_many_values) and [apply#Using_apply_and_built-in_functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Using_apply_and_built-in_functions) – el-teedee Mar 03 '19 at 15:11
  • pretty much feels like native lenses support to me – Jivan Mar 03 '19 at 15:25
1

Without nesting that can easily be done using Object.assign:

Object.assign({}, obj, { label: "New One" })

or using object spreading:

{ ...obj, label: "New One" }

to support nested objects / arrays you have to manually merge the objects recursively:

 function merge(target, changes) {
   const result = {};

   for(const [key, value] of Object.entries(target).concat(Object.entries(changes))) {
     if(Array.isArray(value)) {
       // TODO
     } else if(typeof value === "object") {
       result[key] = merge(result[key] || {}, value);
     } else {
       result[key] = value;
     }
   }

   return result;
}
Jonas Wilms
  • 132,000
  • 20
  • 149
  • 151
0

Another alternative way is:

let newObj = JSON.parse(JSON.stringify(oldObj))
Kubwimana Adrien
  • 2,463
  • 2
  • 8
  • 11