Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant way to copy only a part of an object [duplicate]

I would like to create an new object from a bigger one, by copying only a few properties from it. All the solutions I know are not very elegant, I wonder if there is a better choice, native if possible (no additional function like at the end of the following code)?

Here is what I usually do for now:

// I want to keep only x, y, and z properties:
let source = {
    x: 120,
    y: 200,
    z: 150,
    radius: 10,
    color: 'red',
};

// 1st method (not elegant, especially with even more properties):
let coords1 = {
    x: source.x,
    y: source.y,
    z: source.z,
};

// 2nd method (problem: it pollutes the current scope):
let {x, y, z} = source, coords2 = {x, y, z};

// 3rd method (quite hard to read for such simple task):
let coords3 = {};
for (let attr of ['x','y','z']) coords3[attr] = source[attr];

// Similar to the 3rd method, using a function:
function extract(src, ...props) {
    let obj = {};
    props.map(prop => obj[prop] = src[prop]);
    return obj;
}
let coords4 = extract(source, 'x', 'y', 'z');
like image 340
yolenoyer Avatar asked Jul 14 '18 15:07

yolenoyer


People also ask

How can we copy object without mutating?

Similar to adding elements to arrays without mutating them, You can use the spread operator to copy the existing object into a new one, with an additional value. If a value already exists at the specified key, it will be overwritten.

What is a shallow copy JavaScript?

Shallow copy is a bit-wise copy of an object. A new object is created that has an exact copy of the values in the original object. If any of the fields of the object are references to other objects, just the reference addresses are copied i.e., only the memory address is copied.

What is shallow copy and Deepcopy in JavaScript?

shallow copying. A deep copy means that all of the values of the new variable are copied and disconnected from the original variable. A shallow copy means that certain (sub-)values are still connected to the original variable. To really understand copying, you have to get into how JavaScript stores values.


6 Answers

One way to do it is through object destructuring and an arrow function:

let source = {
    x: 120,
    y: 200,
    z: 150,
    radius: 10,
    color: 'red',
};

let result = (({ x, y, z }) => ({ x, y, z }))(source);

console.log(result);

The way this works is that the arrow function (({ x, y, z }) => ({ x, y, z })) is immediately called with source as the parameter. It destructures source into x, y, and z, and then immediately returns those as a new object.

like image 161
Marco Avatar answered Oct 05 '22 00:10

Marco


Just take a function.

const extract = ({ x, y, z }) => ({ x, y, z });

let source = { x: 120, y: 200, z: 150, radius: 10, color: 'red' };

console.log(extract(source));

Another solution is a destructuring to a target object with target properties.

let source = { x: 120, y: 200, z: 150, radius: 10, color: 'red' }, 
    target = {};

({ x: target.x, y: target.y, z: target.z } = source);

console.log(target);
like image 28
Nina Scholz Avatar answered Oct 05 '22 02:10

Nina Scholz


You can do it like below via Spread Operator

let source = {
    x: 120,
    y: 200,
    z: 150,
    radius: 10,
    color: 'red',
};

let {radius, color, ...newObj} = source;
console.log(newObj);
like image 38
Vikasdeep Singh Avatar answered Oct 05 '22 01:10

Vikasdeep Singh


IIFE with destructuring maybe?:

const coords = (({x, y, z}) => ({x, y, z}))(source);
like image 23
Jonas Wilms Avatar answered Oct 05 '22 00:10

Jonas Wilms


For simple cases like this the object destructuring mentioned in other answers is very neat but tends to look a bit cumbersome when dealing with larger structures as you double up on property names.

Expanding on your own answer - if you were going to write an extract utility (I'll roll my own for fun)... you can make it more flexible by currying it - allowing you to swap the order of the arguments (notably putting the data source last) while still being variadic in accepting property names.

I'd consider this signature: extract = (...props) => src => { ... } more elegant as it allows for a greater degree of reuse in composing new, named functions:

const extract = (...props) => src => 
    Object.entries(src).reduce(
        (obj, [key, val]) => (
            props.includes(key) && (obj[key] = val), 
            obj
    ), {})

const getCoords = extract('x', 'y', 'z')

const source = {
    x: 120,
    y: 200,
    z: 150,
    radius: 10,
    color: 'red'
}

console.log(getCoords(source))
like image 33
Emissary Avatar answered Oct 05 '22 00:10

Emissary


1st method is elegant and readable.

Please don't obfuscate simple operations by some workarounds. Other people who will need to maintain this code, including future yourself will be very thankful in the future.

like image 30
Napas Avatar answered Oct 05 '22 01:10

Napas