reconstruct partial deconstructed object es6 - javascript

I am looking for the most concise way to have a new object out of the fields of the deconstructed one.
let obj = {
a: 1,
b: 2,
c: null
}
Currently I have:
let {a, c} = obj;
let data = {a, c}
What I wished I would be having:
let data = {a, c} = obj;
// but data actually becomes {a, b, c}
// and not {a, b} as I wished it to be.

Although your code looks fine may be if your task consists in cherrypicking keys and put it in another object destructuring is not the best approach:
const object = { a: 5, b: 6, c: 7 };
const picked = (({ a, c }) => ({ a, c }))(object);
console.log(picked)

You can define a function that will provide the destructured object as the return value, and assign the call to data:
const obj = {
a: 1,
b: 2,
c: null
}
const partial = ({ a, c }) => ({ a, c })
const data = partial(obj)
console.log(data)
Unfortunately this isn't possible in one line without some setup, but the setup is worthwhile if you are creating the same partial object a lot of places in your source.

Rather than rely on destructuring for this, you can implement a version of the commonly found "pick" function, that accepts as input an object and an array of keys to pull out of that object:
function pick(obj, keys) {
return keys.reduce((memo, key) => {
memo[key] = obj[key];
return memo;
}, {});
}
const obj = { a: 1, b: 2, c: 3 }
const data = pick(obj, ['a', 'b']);
console.log(data); // { a: 1, b: 2}
Normally I would consider performance less important than readability, which is highly subjective. But in this case, both the pick solution above and the one-liners are orders of magnitude slower than your original two-liner, though pick wins out over the one-liner by a comparatively small margin: https://jsperf.com/testaagwt14124oih1oij

Related

Destructure from dynamic key

Suppose I have some key-value object. I want to destructure dynamically from some key such that I can remove it and just get the remaining items in a new object.
const omit = (obj, key) => {
const { [key], ...rest } = obj // Syntax error
return rest
}
omit({ b: 1, c: 2, d: 3 }, 'd')
// desired output { b: 1, c: 2 }
Is there a way to do that?
Disclaimer: I know there are lots of workarounds but would like to do it with destructuring.
In order to destructure on a dynamic key you will need to provide an alias for JS to bind that value to.
Firefox even gives you a helpful error message here:
const omit = (obj, key) => {
const { [key]: _, ...rest } = obj
// CHANGE -----^
return rest
}
console.log(omit({ b: 1, c: 2, d: 3 }, 'd'))
You can rename the variables when destructuring, and the left side (preexisting name) can be in brackets like you want.
let {[key]: omitted, ...rest} = a;

What is a good way destructure two objects into one, joining specific keys?

Given two objects with different but known keys - which may or may not be set - what is a good way to combine them into one object using specific keymaps for both?
Say, for example, obj1 can have keys itemA, itemB, and itemC. Another obj2 can have keys item_a, item_b, and item_c, and takes precedence. The keys refer to the same final keys a, b, and c respectively.
This is what I came up with, but it feels unnecessarily convoluted.
// Given these objects:
const obj1 = { itemA: 1, itemB: 2, itemC: undefined }
const obj2 = { item_a: undefined, item_b: 20, item_c: 30 }
// Desired result: { a: 1, b: 20, c: 30 }
const filter = obj => Object.fromEntries(
Object.entries(obj).filter(entry => entry[1])
)
const serialized1 = (object =>
({ itemA:a, itemB:b, itemC:c } = object)
&& filter({a, b, c}))(obj1)
const serialized2 = (object =>
({ item_a:a, item_b:b, item_c:c } = object)
&& filter({a, b, c}))(obj2)
const finalObject = { ...serialized1, ...serialized2 }
console.log(finalObject)
// { a: 1, b: 20, c: 30 }
I'm roughly trying to have a keymap for each object for which foreign keys map to my local keys, which then merge.
Note: The mentioned key names are examples. In reality it can be arbitrary, like one of im-dimensions and img-res being stored in a key called size.
Using Stringify and Parse,we can achieve that
const obj1 = { itemA: 1, itemB: 2 };
const obj2 = { item_b: 20, item_c: 30 };
let format=(obj)=>{
return JSON.parse(JSON.stringify(obj).toLowerCase().replace(/\"(\w*)([a-c]{1}\":)/g,"\"$2"));
}
let obj3={...format(obj1),...format(obj2)}
console.log(obj3);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I found a solution. You can create a map of oddly named keys to their preferred names, and "rename" them into a new object using the ES6 Initializer Specification. Basically that means you can use a variable as a key name like so:
const obj = {
[key]: value
}
So with our map, we can create a new key with the value from our old object:
const obj = {
[map[key]]: obj[key]
}
In order to automate this for every key in an object, we can use the reduce function in conjunction with the spread syntax:
const mapper = (map, obj) =>
Object.keys(obj)
.filter(key => Boolean(obj[key]))
.reduce((acc, key) =>
({
...acc,
...{ [map[key]]: obj[key] }
}), {})
This is a very versatile and reusable function. All we need is to supply the map and the input object. Here's a full example based on the data from the original question:
// Given these objects:
const obj1 = { itemA: 1, itemB: 2, itemC: undefined }
const obj2 = { item_a: undefined, item_b: 20, item_c: 30 }
// Desired result: { a: 1, b: 20, c: 30 }
// ------
// Map keys to predefined attributes
const map = {
itemA: 'a',
item_a: 'a',
itemB: 'b',
item_b: 'b',
itemC: 'c',
item_c: 'c'
}
const mapper = (map, obj) =>
Object.keys(obj)
.filter(key => Boolean(obj[key]))
.reduce((acc, key) =>
({
...acc,
...{ [map[key]]: obj[key] }
}), {})
const keymap = mapper.bind(null, map)
const attributes = { ...keymap(obj1), ...keymap(obj2) }
console.log(attributes)
// { a: 1, b: 20, c: 30 }

One liner function to iterate over array of objects and change one value

Let's say I have an array of objects like this:
let x = [{'a': 0.288888, 'b': 0.5}, {'a':9.565464645654, 'b': 0.6}];
I want to iterate over this and change the number to fixed length. I tried
x.map((data) => data.a.toFixed(3))
# ["0.289", "9.565"]
Expected Output:
[{'a': 0.289, 'b': 0.5}, {'a':9.565, 'b': 0.6}]
One way to make it work would be:
x.map((data) => {
data.a = data.a.toFixed(3);
return data;
});
Is there any other/better way than this?
Use forEach and mutate the existing object:
const x = [{'a': 0.288888, 'b': 0.5}, {'a':9.565464645654, 'b': 0.6}];
x.forEach((data) => data.a = data.a.toFixed(3));
console.log(x);
This is doing the same thing that your .map is doing, in one line, but .map should only be done to create a new array - if you're looking for side-effects only, use forEach.
If you don't want to mutate and want to create a new array, then return a new object in the .map:
const x = [{'a': 0.288888, 'b': 0.5}, {'a':9.565464645654, 'b': 0.6}];
const changedX = x.map(({ a, b }) => ({ a: a.toFixed(3), b }));
console.log(changedX);
If you're aiming for code readability (which is probably the most important thing for most scripts), I'd recommend against trying to squash everything onto one line - it doesn't make things more readable, often it's the opposite.
To make the first snippet pass the no-return-assign and arrow-parens rules, put brackets around the function body so that it's not a concise body:
x.forEach((data) => { data.a = data.a.toFixed(3) });
Demo
Try using forEach loop
x.forEach((elem) => elem.a = elem.a.toFixed(3));
Hope it helps.
Here is one-liner. Almost same as your map method call, But just use ({}) as return value to avoid explicit return statement.
let x = [
{ a: 0.288888, b: 0.5 },
{ a: 9.565464645654, b: 0.6 }
];
const updated = x.map(data => ({
...data,
a: data.a.toFixed(3)
}));
console.log(updated);
// with destructuring
let x = [
{ a: 0.288888, b: 0.5 },
{ a: 9.565464645654, b: 0.6 }
];
const updated = x.map(({ a, b }) => ({
a: a.toFixed(3),
b,
}));
console.log(updated);
You can use Destructing assignment:
let x = [
{ a: 0.288888, b: 0.5 },
{ a: 9.565464645654, b: 0.6 }
];
x = x.map(data => ({...data, a: data.a.toFixed(3)}));
console.log(x);

How to use a destructuring assignment inside an object

Is it possible to use a destructuring assignment inside an object?
This works
const test = {a: 'hey', b: 'hello'}
const {a,b} = test;
const destruct = {
a,
b
};
Would like to do this
const test = {a: 'hey', b: 'hello'}
// something like this
const destruct = {
{a,b}: test
};
const destruct = {
{a}: test,
{b}: test
};
If I understand correctly, it seems the spread syntax is a good fit for what you need.
The spread syntax "..." allows you to "spread" the key/value pairs from a source object (ie test) to a target object (ie destruct):
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
}
const destruct = {
// {a,b}: test <-- invalid syntax
...test // equivalent using the "spread" syntax
};
console.log(destruct)
Additionally, if you wanted to select a subset of keys from a source object and spread those into a target object then this can be achieved by the following:
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
}
/* Spread subset of keys from source object to target object */
const welcomeOnly = {
...({ a, b } = test, { a, b })
}
console.log('exclude goodbye, show welcomes only:', welcomeOnly);
The second example works by destructing the source object (ie test) into an object, with the subset of keys that we want (a and b).
In the scope of that expression (ie everything between the ( and )), these keys are accessible as local variables. We take advantage of this, and pass those to a new object (ie { a, b }). Because the new object is declared after the ,, it is returned as the result of the expression.
If you are trying to take a subset of properties you can use the rest operator
const test = {
a: 'hey',
b: 'hello',
c: 'goodbye'
};
const { c, ...destruct } = test;
console.log(destruct);
This assigns c to a const and the the left over properties are assigned to the const destruct. List all the unwanted properties first and then the left over properties are caught with the rest operator.
Works with arrays as well.
const test = ['hey', 'hello', 'goodbye'];
const [ first, ...rest ] = test;
console.log(rest);
You can try to work like this for destructuring arrays!
let abc = {
a: 'hello',
b: 'hey',
c: 'hi, there!'
}
let {a: x, b:y, c:z} = abc;
console.log(x,y,z)
// "hello"
"hey"
"hi, there!"

Why does inline destructuring as a function argument not work as expected

Let's say I have code that requires access to a state object that looks like this
const store = {
getState: () => ({
a: "test",
b: 1,
c: 23,
})
}
And a helper function that looks like this, the rest is to illustrate the fact that the output of this will not be JUST the destructured argument I passed in.
function printState ({a, b, ...rest}) {
console.log(a, b, rest)
}
So if I try to use inline destructuring
printState({ a, b } = store.getState())
The output of above is
"test" 1 Object {
c: 23
}
I would have expected only a and b to be passed in, and not the rest of the object. Does anyone know why this is the case?
{ a, b } = store.getState() is an assignment expression and the value returned by that will always be the expression on the right hand side which is store.getState().
If you want only a and b then you can use and IIFE.
const store = {
getState: () => ({
a: "test",
b: 1,
c: 23,
})
}
function printState ({a, b, ...rest}) {
console.log(a, b, rest)
}
printState((({a,b}) => ({a,b}))(store.getState()))
printState({ a, b } = store.getState())
^^^^^^^^^^^^^^^^^^^^^^^^^^^
since it's an assignment expression which returns the right hand side value, so your this function invocation is actually evaluated something like this
function printState(_ref) {
var a = _ref.a,
b = _ref.b,
rest = _objectWithoutProperties(_ref, ["a", "b"]);
console.log(a, b, rest);
}
printState((_store$getState = store.getState(),
a = _store$getState.a,
b = _store$getState.b,
_store$getState)
);
so you actually end up with printState(store.getState())
You can paste your code here and see Babel

Categories