Combine values of two dynamic keys in an object - javascript

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);

Related

get data the object with a filter

Hi guys i have a question with objects in Javascript.
Ok, i've this structure
My Object
{
"client": {
"rpc2": {
"testEstrobos": false
},
"rpc1": {
"testParlantes": false
},
"channelServerId": 0,
"channelClientStatus": false,
"testParlantes": false
},
"shared": {
"atributoServidor": "modificadoporTenant"
}
}
I want to obtain{ rpc1: { testEstrobos: false}, rpc2: { testParlantes: false } } but only with one condition. JSON can updated because at the end it is obtained by an API. I would need some type of filter like the funciĆ³n Like in SQL were I only obtain an RPC. I used the function match and indexOf. This last one does filter but would only give me the name without its structure
It's a little bit tricky. First get the keys of your client-object and filter over it for all property with keyname beginning of 'rpc'. Then interate with foreach over the keys. For every key add to a new object a new property with this keyname and it's property.
let obj = {
"client": {
"rpc2": {
"testEstrobos": false
},
"rpc1": {
"testParlantes": false
},
"channelServerId": 0,
"channelClientStatus": false,
"testParlantes": false
},
"shared": {
"atributoServidor": "modificadoporTenant"
}
};
let res = {};
Object.keys(obj.client).filter(key => key.substr(0,3)==='rpc').forEach(key => res[key] = obj.client[key]);
console.log(res);

Updating state as a nested object

I have this type of state in my app
state = {
abc: true,
array: [
{ id: 12345, done: false },
{ id: 10203, done: false },
{ id: 54321, done: false }
]
};
I am looking for a solution to the following problem: I need to change done property accordingly to passed id like in the following function when something like this handle(12345) is passed as an argument to handle function :
handle = id => {
this.state.array.map(e => {
if (e.key === id) {
this.setState({array: [
{ id: id, done: true },
{ id: 10203, done: false },
{ id: 54321, done: false }
]})
}
});
};
In simple words I need to change just one object in array based on provided id.
Thanks for any help or tips!
I'd write the handle method as:
handle = id => {
this.setState(prevState => {
const { array } = prevState;
return {
array: [
...array.filter(o => o.id !== id),
{id, done: true}
]
};
});
};
The idea here is that, remove the matching id from old state, and then add a new object to the array with id and done property as {id, done: true}.
Once you are allowed to restructure state to be hashmap instead of array:
state = {
abc: true,
array: {
12345: { done: false },
10203: { done: false },
54321: { done: false }
]
};
then you will be able to use power of spread operator:
let id = 12345;
this.setState({
array: {
...this.state.array,
[id]: {
...this.state.array[id],
done: true
}
}
});
Otherwise using array.map() seems to be the only option
You can use this Redux pattern to return a new array with the element in question being changed, while keeping your array immutable:
handle = id => {
const updatedArray = this.state.array.map(e => {
if (e.key === id) {
return { id: id, done: true };
}
else {
return e;
}
});
this.setState({array: updatedArray});
};
This keeps your data structures immutable while changing only what you need to change.
var newArray = this.state.array;
for(var i = 0; i < newArray.length; i++){
if(newArray[i].id === 12345) {
newArray[i].done = true;
}
}
this.setState({array: newArray});
By creating the newArray here you will be avoiding directly touching the state element, so you can change anything you want inside it afterwards you can set the state.

How to set value of an immutable state in Javascript?

Given an immutable state like this:
alerts: {
5a8c76171bbb57b2950000c4: [
{
_id:5af7c8652552070000000064
device_id:5a8c76171bbb57b2950000c4
count: 1
},
{
_id:5af7c8722552070000000068
device_id:5a8c76171bbb57b2950000c4
count: 2
}
]
}
and an object like this:
{
_id:5af7c8652552070000000064
device_id:5a8c76171bbb57b2950000c4
count: 2
}
I want to replace the object with the same id in the alerts state (immutable), such that end result looks like this:
alerts: {
5a12356ws13tch: [
{
_id:5af7c8652552070000000064
device_id:5a8c76171bbb57b2950000c4
count: 2
},
{
_id:5af7c8722552070000000068
device_id:5a8c76171bbb57b2950000c4
count: 2
}
]
}
How can I do that? With mergeDeep, getIn, setIn, and updateIn, found on List, Map or OrderedMap ?
I tried doing something like this.. where index is 0 and deviceId is 5a12356ws13tch
Does not work though.
export const oneAlertFetched = (state, {deviceId, index, alert}) => state.setIn(['alerts', deviceId, index], alert).merge({fetching: false})
I tried this as well. Does not work.
export const oneAlertFetched = (state, {deviceId, index, alert}) => {
const a = state.alerts[deviceId][index]
state.alerts[deviceId][index] = Object.assign({}, a, alert)
return
}
By immutable, you mean that your property is non-writable.
If you want to modify your object in-place (not recommended), you will need the property to be at least configurable:
const device = alerts['5a12356ws13tch'][0];
if (Object.getOwnPropertyDescriptor(device, 'count').configurable) {
// Manually make it `writable`
Object.defineProperty(device, 'count', {
writable: true
});
// Update property's value
device.count++;
// Set it back to `non-writable`
Object.defineProperty(device, 'count', {
writable: false
});
}
console.log(device.count); // 2
If it is not configurable (cannot make it writable), or you do not want to jeopardize your application (it must be non-writable on purpose), then you should work on copies.
const device = alerts['5a12356ws13tch'][0];
alerts['5a12356ws13tch'][0] = Object.assign({}, device, {count: device.count + 1});
Object.assign() works on flat objects. If you need deep copy, have a look at my SO answer there.
I think you mean you want to return a new object with the updated payload?
function getNextAlerts(alerts, parentDeviceId, payload) {
const alertsForDevice = alerts[parentDeviceId];
if (!alertsForDevice || alertsForDevice.length === 0) {
console.log('No alerts for device', deviceId);
return;
}
return {
...alerts,
[parentDeviceId]: alerts[parentDeviceId].map(item =>
item._id === payload._id ? payload : item
),
}
}
const alerts = {
'5a12356ws13tch': [
{
_id: '5af7c8652552070000000064',
device_id: '5a8c76171bbb57b2950000c4',
count: 1
},
{
_id: '5af7c8722552070000000068',
device_id: '5a8c76171bbb57b2950000c4',
count: 2
}
]
};
const nextAlerts = getNextAlerts(alerts, '5a12356ws13tch', {
_id: '5af7c8652552070000000064',
device_id: '5a8c76171bbb57b2950000c4',
count: 2,
});
console.log('nextAlerts:', nextAlerts);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
If you're working with plain JavaScript objects and want to keep "immutable" approach you have to use spreads all over the nested structure of state object.
But, there are some tools already targeting this issue - lenses.
Here is the example of both approaches, array/object spreads and lenses - ramda repl.
In short, your example via spreads:
const oneAlertFetched = (state, { deviceId, index, alert }) => ({
...state,
alerts: {
...state.alerts,
[deviceId]: [
...state.alerts[deviceId].slice(0, index),
{ ...state.alerts[deviceId][index], ...alert },
...state.alerts[deviceId].slice(index + 1)
],
}
})
And via lenses using Ramda's over, lensPath, merge and __*:
const oneAlertFetched = (state, { deviceId, index, alert }) =>
R.over(
R.lensPath(['alerts', deviceId, index]),
R.merge(R.__, alert),
state
)
* R.__ placeholder used to swap 1st & 2nd parameters of R.merge
PS: lenses solution is intentionally adjusted to match the declaration of your function, so you can easily compare two approaches. However, in real life, with such powerful and flexible tool, we can rewrite the function to be more readable, reusable, and performant.

Object push Firebase, how to remove key names from pushed items

I have this Object.key code that pushes all items:
const cloned_items = [];
Object.keys(items).sort().map(key => {
let item = {
[`item-${uid}`]: {
item: false
}
}
cloned_items.push({ ...item });
});
database.ref('/app/items').update({
...cloned_items
})
but this produces following result:
"0" : {
"timeslot-87dah2j" : {
item: false
}
},
"1" : {
"timeslot-7s1ahju" : {
item: false
}
}
instead of:
"timeslot-87dah2j" : {
item: false
},
"timeslot-7s1ahju" : {
item: false
}
any idea ?
It seems like you want to create a plain object, not an array.
In that case:
const cloned_items = Object.assign(...Object.keys(items).map(uid =>
({ [`item-${uid}`]: {item: false} })
));
NB: sorting is of no use when creating an object -- its keys are supposed to have no specific order.
You're creating an array of objects. Seems like you want to use .reduce() to create a single object from the array.
const cloned_items = Object.keys(items).sort().reduce((obj, key) =>
Object.assign(obj, { [`item-${uid}`]: { item: false } })
, {});
Your code doesn't show where uid is coming from, but I assume you meant key there, along with timeslot instead of item.
You may find Object.defineProperty to be cleaner, though you'll need to set up the property descriptor as you want it.
const cloned_items = Object.keys(items).sort().reduce((obj, key) =>
Object.defineProperty(obj, `item-${uid}`, {value:{item: false}})
, {});

Change property in array with Spread Operator returns object instead of array

I want to change the property of an object similar to this, this is a simplified object with a few properties of the original:
state = {
pivotComuns: [
{
id: 1,
enabled : true
},
{
id: 2,
enabled : true
}
],
otherProperties : "otherProperties"
}
I'm changing the state of enabled like this:
state = {
...state,
pivotColumns: {
...state.pivotColumns,
[2]: {
...state.pivotColumns[2], enabled: !state.pivotColumns[2].enabled
}
}
}
It works, but instead of return an array like I is the pivotComuns property it returns an object, "notice that I change [] for {}":
state = {
pivotComuns: {
{
id: 1
enabled : true
},
{
id: 2,
enabled : true
}
},
otherProperties : "otherProperties"
}
What I'm doing wrong, I need to keep that property an array.
Very late post, but for future reference, you could do the following:
state = {
...state,
pivotColumns: state.pivotColumns.map(pc =>
pc.id === 2 ? {...pc, enabled:!pc.enabled} : pc
)
}
The advantage is that you will not change the object referenced in the "old array", you will instead insert a new object in its place. So if you would like to go back and forth in the state you can now do so.
example:
https://codepen.io/anon/pen/JyXqRe?editors=1111
I don't believe you can use the spread operator in such a way and in fact wouldn't recommend it if you could because it creates very hard to read code. There is a much simpler solution that I use on a daily basis when it comes to updating a key/value on an object where the value is an array:
var state = {
pivotColumns: [
{
id: 1,
enabled : true
}, {
id: 2,
enabled : true
}
],
otherProperties : "otherProperties"
}
var clonedPivotColumns = state.pivotColumns.slice();
clonedPivotColumns[1].enabled = !state.pivotColumns[1].enabled;
state = {
...state,
pivotColumns: clonedPivotColumns
}
this will get you the right results and will not cause any mutations.
working pen
http://codepen.io/finalfreq/pen/ggdJgQ?editors=1111

Categories