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');
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.
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);
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);
IIFE with destructuring maybe?:
const coords = (({x, y, z}) => ({x, y, z}))(source);
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))