Lodash merge properties of object that are not in object already - javascript

I have an object:
var props = {//values here};
this.options: {
cssSelectorAncestor: props.cssSelectorAncestor,
media: {
autoPlay: props.autoPlay,
muted: props.muted
}
}
this.options = _.merge(
{
defaultPlaybackRate: 1.0,
minPlaybackRate: 0.5
}, props);
How do I merge all of the properties in props apart from the ones that have already been assigned to options.
Consider props immutable.

I'm just guessing here, because I'm having trouble following your sample code, but I think you want defaults():
const result = _.defaults({ foo: true }, { foo: false, bar: true });
// ➜ { foo: true, bar: true }
This will only merge in properties that don't already exist.

Merge all three objects into an empty object. Make sure you do it in the right order. The keys with highest priority go on the right.

Related

Combine values of two dynamic keys in an object

I have an object that looks like this:
const query = {
item: {
'available': false
},
status: {
'locked': true
}
}
I want to fetch the nested keys available and locked and create a new object with their values, resulting in
{
'available': false,
'locked': true
}
Whats the most precise way to do this? Bear in mind that the keys and the nested keys are dynamic, i.e. can change in future depending on another object.
You could get the values and create a new object.
const
query = { item: { available: false }, status: { locked: true } },
joined = Object.assign({}, ...Object.values(query));
console.log(joined);
Use spread operator:
const query = {
item: {
'available': false
},
status: {
'locked': true
}
}
const value = {...query.item, ...query.status};
console.log(value);

using es6 reduce() to manipulate an object using another object

I have state object as:
this.state = {
letterType: {},
letterTag: {},
departmentName: {},
assignedOfficer: {}
}
and I have an another object sortFilters as:
sortFilters = {
letterType: {
0: "letterType1",
1: "letterType2"
},
letterTag: {},
departmentName: {
0: "dept1"
},
assignedOfficer: {}
}
now what I want is to create a newState object (probably using es6 reduce()) which will be created based on sortFilters object such as:
this.newState = {
letterType: {
letterType1: true,
letterType2: true
},
letterTag: {},
departmentName: {
dept1: true
},
assignedOfficer: {}
}
I think this is possible using es6 reduce() but I am not able to get it to work.
As described, your problem is a use case for Object.assign, since you just want to copy the contents of four objects (on sortFilters) into your state object. You'd either do it manually:
Object.assign(this.newState.letterType, sortFilters.letterType);
Object.assign(this.newState.letterTag, sortFilters.letterTag);
Object.assign(this.newState.departmentName, sortFilters.departmentName);
Object.assign(this.newState.assignedOfficer, sortFilters.assignedOfficer);
...or in a loop:
for (const name of Object.keys(sortFilters)) {
Object.assign(this.newState[name], sortFilters[name]);
}
That merges the entries from the sortFilters objects with the ones in this.newState. If you want to replace them instead, you'd use assignment (and probably a shallow copy, but that depends on how sortFilters is used later):
this.newState.letterType = Object.assign({}, sortFilters.letterType);
this.newState.letterTag = Object.assign({}, sortFilters.letterTag);
this.newState.departmentName = Object.assign({}, sortFilters.departmentName);
this.newState.assignedOfficer = Object.assign({}, sortFilters.assignedOfficer);
or
for (const name of Object.keys(sortFilters)) {
this.newState[name] = Object.assign({}, sortFilters[name]);
}
Note that Object.assign does a shallow copy; if any of these objects are nested, you'll need something else.
As of ES2018, you can use property spread when creating the new objets instead of Object.assign:
for (const name of Object.keys(sortFilters)) {
this.newState[name] = {...sortFilters[name]};
}
There are other methods to achieve the same, as reduce is used over arrays. But if using reduce is a practice, then Yes that is also possible, a rough way could be like the following, where we can use reduce over keys of objects, which is an array.
let state = {letterType: {},letterTag: {},departmentName: {},assignedOfficer: {}}
let sortFilters = {letterType: {0: "letterType1",1: "letterType2"},letterTag: {},departmentName: {0: "dept1"},assignedOfficer: {}}
let newState = Object.keys(state).reduce(function(prev, current) {
let val = sortFilters[current]
if (!val) {
prev[current] = state[current]
} else {
prev[current] = Object.keys(val).reduce(function (p, c) {
p[val[c]] = true
return p
}, {})
}
return prev
}, {})
console.log(newState)
For more details about reduce and Object.keys, please refer to Mozilla Developer Network's documentation.
Not sure what "state" or this.state means in this context...React? Anyhow, tt looks like you just want to simply unpack certain properties and manipulate them. If so destructuring assignment might help. Refer to this article section on Destructuring Nested Objects and this section on Nested object and array destructuring
Demo
let sortFilters = {
letterType: {
0: "letterType1",
1: "letterType2"
},
letterTag: {},
departmentName: {
0: "dept1"
},
assignedOfficer: {}
}
// Making a copy of sortFilters
let final = JSON.parse(JSON.stringify(sortFilters));
// Desruturing final, assigning variables and values
let {
letterType: {
letterType1: letterType1 = true,
letterType2: letterType2 = true
},
letterTag: {},
departmentName: {
dept1: dept1 = true
},
assignedOfficer: {}
} = final;
console.log(letterType1);
console.log(letterType2);
console.log(dept1);

Can Vue.js update a nested property?

I have a complicated component with lots of two way bound variables so in order to keep things clean im grouping variables in category objects.
settings: {
setting1: true,
setting2: false,
setting3: true
},
viewMode: {
option1: true,
option2: false,
options3: true
}
I am then passing the settings to my component like so
<some-component :settings.sync="settings" :viewmode.sync="viewMode"></some-component>
some-component then can transform these values and emit them back to the parent but this is where the problem lies. It appears
this.$emit('update:settings.setting1', newValue)
does NOT work in Vuejs. The only solution i can find to update these values from some-component is to overwrite the entire settings object like so
props: {
settings: {
type: Object,
default: () => {
return {
setting1: true,
setting2: false,
setting3: true
}
}
}
},
computed: {
localSetting1: {
get () {
return this.settings.setting1
},
set (newValue) {
// This does not work
this.$emit('update:settings.setting1', newValue)
// The only thing that does seem to work, is overwriting the entire object
this.$emit('update:settings', {
setting1: newValue,
setting2: this.settings.setting2,
setting3: this.settings.setting3
}
// or to be less verbose, but still update the entire object
this.$emit('update:settings', Object.assign(this.settings, {settings1: newValue}))
}
}
}
This seems a bit messy. Is there not a way to update just a single nested property and emit it back to the parent? The most ideal way being something similar to this
this.$emit('update:settings.setting1', newValue)
Actually Vue can update nested props, we just need to overwrite the whole prop object, as Vue cannot track nested changes.
If we have a prop like this
props: {
someValues: {
'a': 1,
'b': 2,
'c': 3
}
}
If we make a change like this one Vue will not react
this.someValues.a = 10
What we need to do
this.tmpSomeValues = { ...someValues, a: 10 }
this.someValues = this.tmpSomeValues

Extract value of property from object in a Set?

For the example, SomeItem is the model for an object (would be modeled as an interface in Typescript or you can just imagine that there is an item with the form of SomeItem if we are in untyped land.
Say I have a Set: mySet = new Set([{item: SomeItem, selected: true}, ...]).
And I want to check if itemA: SomeItem is selected or not.
What is the cleanest way to do this?
This did not work:
const isSelected = mySet.has({item: itemA, selected: true});
Nor did this:
const isSelected = Array.from(mySet).includes({item: itemA, selected: true});
I'm assuming the above two did not work because it is trying to compare the objects by reference, rather than value.
This does work:
let isSelected: boolean;
mySet.forEach(state => {
if (state.item === itemA) {
isSelected = state.selected;
}
});
But my gut tells me there is a correct way to do this.
So,
How do I extract the value of a property of an object in a Set?
Comparing two objects with the same properties returns true only if they have the same reference, I would suggest to compare their properties as the properties are primitive values..
The array some method can be used to filter if the set contains an specific object
let mySet = new Set([{item: 'SomeItem', selected: true}]);
let itemA = "SomeItem";
let isSelected = Array.from(mySet).some(element => element.item === itemA);
console.log(isSelected);
Let's take a look at it this way, Sets mainly return an iterable. Sure, they're hashed in order, as is a Map, but from the looks of your data structure, a Map would benefit you more here.
const x = new Set([
{ "foo": 1, selected: true },
{ "bar": 1, selected: false },
{ "baz": 1, selected: false },
{ "barf": 1, selected: false },
]);
Now, to get what you're looking for, you'll need as you did, convert to an array using Array.from (or [...x] spread it) and iterate, finding they key.
Now, as a Map:
const y = new Map();
y.set("foo", { selected: true });
y.set("bar", { selected: false });
y.set("baz", { selected: false });
y.set("barf", { selected: false });
With this, you simply change the structure slightly to give item1 or whatever you use the Map key, and set whatever elements you want.
y.has("foo"); // true
y.get("foo").selected; //true
So if you wanted here, it's much easier to grab the iterable key name and get which Map index has the property you want

Destructuring deep properties

I recently started using ES6's destructuring assignment syntax and started to get familiar with the concept. I was wondering if it's possible to extract a nested property using the same syntax.
For example, let's say I have the following code:
let cagingIt = {
foo: {
bar: 'Nick Cage'
}
};
I know I am able to access extract foo into a variable by doing:
// where foo = { bar: "Nick Cage" }
let { foo } = cagingIt;
However, is it possible to extract a deeply nested property, like bar. Perhaps something like this:
// where bar = "Nick Cage"
let { foo[bar] } = cagingIt;
I've tried finding documentation on the matter but to no avail. Any help would be greatly appreciated. Thank you!
There is a way to handle nested objects and arrays using this syntax. Given the problem described above, a solution would be the following:
let cagingIt = {
foo: {
bar: 'Nick Cage'
}
};
let { foo: {bar: name} } = cagingIt;
console.log(name); // "Nick Cage"
In this example, foo is referring to the property name "foo". Following the colon, we then use bar which refers to the property "bar". Finally, name acts as the variable storing the value.
As for array destructuring, you would handle it like so:
let cagingIt = {
foo: {
bar: 'Nick Cage',
counts: [1, 2, 3]
}
};
let { foo: {counts: [ ct1, ct2, ct3 ]} } = cagingIt;
console.log(ct2); // prints 2
It follows the same concept as the object, just you are able to use array destructuring and store those values as well.
You can destructure a property "as" something else:
const { foo: myFoo } = { foo: 'bar' } // myFoo == 'bar'
Here foo was destructured as myFoo. You can also destructure an object "as" a destructured object
const { foo: { bar } } = { foo: { bar: 'baz' } } // bar == 'baz'
Only one variable was defined in each situation, myFoo and bar, and you can see how they are in similar locations as well, except bar has the { } around it.
You can do this for as many layers of nesting as you like, but if you aren't careful going too many level deep you'll get the old "Cannot read properties of undefined(reading 'foo')".
// Here's an example that doesn't work:
const foo = { bar: { notBaz: 1 } };
const {
bar: {
baz: { // baz is undefined in foo, so by trying to destructure it we're trying to access a property of 'undefined'
qux
}
}
} = foo;
// throws Uncaught TypeError: Cannot read properties of undefined (reading 'baz')
// because baz is 'undefined'
// Won't run due to error above
console.log(qux);
In this case it should be obvious that we shouldn't be trying to destructure it because we can see the definition of foo on the previous line doesn't define the property baz. If the object is coming from an API, though, you aren't always guaranteed that every nested property of your expected result will be non-null or not undefined and you can't know beforehand.
You can set a default value for a destructured object by adding = {}:
const { foo: myFoo = 'bar' } = { baz: 'qux' }; // myFoo == 'bar'
const { bar: { baz } = {} } = { qux: 'quuz' } // baz == undefined
// baz is destructured from the object that was set as the default for foo, which is undefined
// this would throw without the default object, as were trying to destructure from 'undefined'
You can do this for deeply nested destructurings:
// Here's an example that works:
const foo = { bar: { notBaz: 1 } };
const {
bar: {
baz: {
qux // you can default the final item to anything you like as well including null or undefined, but it will be undefined in this case
} = {} // if 'baz' undefined, return {}
} = {} // if 'bar' undefined, return {}
} = foo;
console.log(qux); // logs 'undefined'
If any property is null or undefined along the way, it will cause a cascade of returning empty objects, whose properties to be destructured at the next level will just be undefined. This gets out of hand really quickly though with deeper objects, which can be many lines of code with this formatting. Here's another option that does the same exact thing.
const foo = { bar: { notBaz: 1 } };
const {qux} = foo?.bar?.baz ?? {}; // optional chaining and nullish coalescing
If at any point along the way foo, bar, or baz is null or undefined or null, it will return an empty object that you can destructure( the empty object after ??.
It doesn't make much sense to use destructuring on { qux } if you only need to extract one property, though, because this also requires us to add the nullish coalesced value ?? {}. Below is probably better.
const foo = { bar: { notBaz: 1 } };
const { qux, quux, quuz } = foo?.bar?.baz ?? {}; // this method is better for multiple properties
const quxFromBaz = foo?.bar?.baz?.qux; // this method is better for single properties
For me personally, I think it looks a little messy to include all the optional chaining question marks, but it's better than the alternative with nested destructuring and default values at every level.
And it works with arrays
const foo = {
bar: [
{ a: 1 },
{ b: 2 }
]
}
const c = foo?.bar?.[2]?.c // c == undefined
// bar[2] will be undefined, so trying to access property 'c' would normally throw an error
If you have lodash installed, you can use one of the following:
_.get
var object = { 'a': [{ 'b': { 'c': 3 } }] };
_.get(object, 'a[0].b.c');
// => 3
or if you need multiple keys.
_.at
var object = { 'a': [{ 'b': { 'c': 3 } }, 4] };
_.at(object, ['a[0].b.c', 'a[1]']);
// => [3, 4]
You can also safely pair _.at() up with with Array destructuring. Handy for json responses.
[title, artist, release, artwork] = _.at(object, [
'items[0].recording.title',
'items[0].recording.artists[0].name',
'items[0].recording.releases[0].title',
'items[0].recording.releases[0].artwork[0].url'
]);
Three Levels Deep
In case this helps anyone, here's a bit of code that shows how to destructure three levels deep. In this case, I'm using the find() method on an array.
const id = someId
array.find(({ data: { document: { docId }, }, }) => docId == id)
Above, the array data is structured like this (each obj in the array is the same shape):
[{
isSuccess: true,
isLoading: false,
data: {
foo: bar,
...,
document: {
docId: '123',
...
},
}}]

Categories