How to assign in nested object using Object.assign()? - javascript

My main object looks like this:
const obj = {
data: {
'Puppies' : [],
'Kittens': []
}
};
I want to assign a value to the data field in data.Puppies[0].
When I try to do this using Object.assign() I get an error:
Unexpected token, expected , (83:12)
81 |
82 | return Object.assign({}, obj, {
> 83 | data.Puppies[0]: list
| ^
84 | });
85 | }
86 |
I'm not sure how I can accomplish my task.
I need to use Object.assign() because I need to return a new object not the original one. I am doing this because of a Redux reducer in ReactJS.

ES6 way without mutations:
const obj = {
data: {
'Puppies' : [],
'Kittens': []
}
};
const data = Object.assign({}, obj.data, {
Puppies: [...obj.data.Puppies, 'newValue']
});
const newObject = Object.assign({}, obj, {data});
console.log(newObject);

You could assign the outer object and assign to the array the new content at index 0 without mutating the original obj.
const list = ['foo', 'bar'];
const obj = { data: { Puppies: [], Kittens: [] } };
console.log(Object.assign({}, obj, { data: { Puppies: Object.assign([], obj.data.Puppies, { 0: list }) } }));
console.log(obj);
The same with content for Puppies.
const list = ['foo', 'bar'];
const obj = { data: { Puppies: ['bar', 42], Kittens: [] } };
console.log(Object.assign({}, obj, { data: { Puppies: Object.assign([], obj.data.Puppies, { 0: list }) } }));
console.log(obj);

You could try using Ramdajs lens to achieve this
const obj = {
data: {
'Puppies' : [],
'Kittens': []
}
};
const list = [1,2,3,4];
const xlens = R.lensPath(['data', 'Puppies'])
console.log(R.set(xlens, list, obj))
Here is the code repel

Replace Puppies with a new array with list as element 0 and the rest of Puppies starting with element 1, so effectively replacing element 0.
var obj = { data: { 'Puppies': [], 'Kittens': [] } };
var list = [ 'a', 'b' ];
var n = Object.assign({}, obj, {
data: Object.assign({}, obj.data, {
Puppies: [ list ].concat(obj.data.Puppies.slice(1))
})
});
console.log(obj);
console.log(n);

Related

How to concert [{ id:1, var1:val1, var2:val2, varX:[time1, time2, time3]}] into [{id:1, var1:val1, var2:val2, varX1:time1, varX2:time2, varX3:time3}]

I am working on a data format transformation which showed on the title, but I don't know how to figure out. I have tried to write the blow code to add the variable name for second dimensional array:
const data = [
{ id: 1, var1: 'val1', var2: 'val2', varX: ['time1', 'time2', 'time3'] },
{ id: 2, var1: 'val2', var2: 'val3', varX: ['time4', 'time5', 'time6'] },
];
const test = data.map((o) => o.varX);
for (i = 0; i < test.length; i++) {
const test2 = test[i].reduce((res, cur, idx) => {
res[`varX${idx}`] = cur;
return res;
}, {});
console.log(test2);
}
but what I expected result should be:
[{id:1, var1:val1, var2:val2, varX1:time1, varX2:time2, varX3:time3},{id:2, var1:val2, var2:val3, varX1:time4, varX2:time5, varX3:time6}]
Could anyone can guide me how to convert the data?
The issue with your code is that you're extracgint the properties that are not varX from the data by mapping only the values that have a varX. Then you do a good job at reducing them, but then you would have to merge the "left over" properties into the new object, it sounds a bit cumbersome to me. Instead, you could do something like the following:
const data = [
{ id: 1, var1: "val1", var2: "val2", varX: ["time1", "time2", "time3"] },
{ id: 2, var1: "val2", var2: "val3", varX: ["time4", "time5", "time6"] },
];
const test = data.map((o) => {
return Object.entries(o).reduce((p, [k, v]) => {
if (k === "varX") {
for (let index = 0; index < v.length; index++) {
p[`varX${index + 1}`] = v[index];
}
} else {
p[k] = v;
}
return p;
}, {});
});
console.log(test);
First, you map the data objects, then for each object you reduce their key/value pairs and check if the property is varX, if so, then you iterate through the array and assign to the new object the varX${index + 1} since you want the first property to be varX1, otherwise you just keep the same key/value pair.
You need create objects in reducemethod instead array, after replace varX to this new object by second map() method
const data = [{'id':1, 'var1':'val1', 'var2':'val2', 'varX':['time1', 'time2', 'time3']},
{'id':2, 'var1':'val2', 'var2':'val3', 'varX':['time4', 'time5', 'time6']}]
const test = data.map(item => item.varX);
const test2 = {}
for (i = 0; i < test.length; i++) {
test2[i] = test[i].reduce((accum, item, index) => {
return { ...accum, [`varX${index}`]: item};
}, {});
}
const dataX = data.map((item, index) => {
delete item.varX
return { ...item, ...test2[index] }
});
console.log(dataX)
Because of the first map in test, you are operating only on values of varX. You can just add another operation to merge the original data[i] objects with your new reduced varX objects.
const data = [
{ id: 1, var1: 'val1', var2: 'val2', varX: ['time1', 'time2', 'time3'] },
{ id: 2, var1: 'val2', var2: 'val3', varX: ['time4', 'time5', 'time6'] },
];
const test = data.map((o) => o.varX);
for (i = 0; i < test.length; i++) {
const test2 = test[i].reduce((res, cur, idx) => {
res[`varX${idx}`] = cur;
return res;
}, {});
// exclude varX and merge your new varX${i} back into data[i]
const {varX, ...test3} = Object.assign(data[i], test2);
console.log(test3);
}
Transform in-place code. I've deliberately avoided reduce, because the syntax becomes less readable and it adds performance overhead. The downside is that this code mutates the original data (transforms the data in-place). Can be easily mitigated by creating copies of objects val while mapping.
[1,2,3,4,5,6].forEach( i => eval(`time${i} = "time${i}";val${i} = "val${i}"`) );
const data = [{id:1, var1:val1, var2:val2, varX:[time1, time2, time3]}, {id:2, var1:val2, var2:val3, varX:[time4, time5, time6]}]
data.forEach((val, i)=>{
val.varX.forEach( (X,i) => val[`varX${i}`]=X );
delete val.varX;
console.log(val);
return val;
});
console.log(data);
I've created a version below using reduce and spread syntax. You can look at the spread syntax as simply an operation to copy properties to the new object being generated. I've used destructuring syntax to isolate varX from the rest of the properties and put them in varX and noVarX.
This also has the advantage of deep copying except for the final outside referenced objects in data.
[1,2,3,4,5,6].forEach( i => eval(`time${i} = "time${i}";val${i} = "val${i}"`) );
const data = [{id:1, var1:val1, var2:val2, varX:[time1, time2, time3]}, {id:2, var1:val2, var2:val3, varX:[time4, time5, time6]}]
const expanded = data.map(val => {
const {varX, ...noVarX} = val;
return {
...noVarX,
...varX.reduce( (res, cur, idx) =>
({ ...res, [`varX${idx}`]: cur }), {})
};
});
console.log(expanded);

how to create nested object from an array with same key (javascript)

arr = ["sadik", "arif", "rahman"]
I want to create a nested object with the same key but different value like:
{
subcategory: {
name: sadik
subcategory: {
name: arif
subcategory: {
name: rahman
}
}
}
}
my code:
let arr = ['sadik', 'arif', 'babor']
let obj = {}
arr.forEach((elem) => {
let a = {}
a["subcategory"] = {name:elem}
Object.assign(obj, a)
})
i get only last value:
{
subcategory: {
name:"babor"
}
}
Your code did not work because you were replacing your "subcategory" key value in each iteration. You have change the reference object to the next nested level each time to get the expected output, see the working snippet below:
const arr = ['level-1', 'level-2', 'level-3']
const obj = {}
let refObj = obj;
arr.forEach( ele => {
refObj = refObj['subcategory'] || refObj;
refObj['subcategory'] = { 'name': ele};
})
console.log('output', obj);

How do I set the value of a key in on array as the key in an other array?

I am trying to get the value feature_1 of a key name from the array data and set feature_1 as the key of another array asset which has an array as value.
Example :
//input:
data: {
name: "feature_1",
value_a: 1,
value_b: 2
}
//Output:
asset: {
feature_1:[1,2]
}
You can try:
var asset = {};
if ('name' in data) {
var tmp = [];
for (k in data){
if (k != 'name') tmp.push(data[k]);
}
asset[data['name']] = tmp;
}
and in case your interpreter supports ES6 you can use, for example:
let {name, ...v} = data;
let asset = {[name]: Object.values(v)};
If supports same keys in a JSON object ( It seems not ) You can do it like this:
let data= {
name: "feature_1",
value_a: 1,
value_b: 2,
value_x: 500,
name: "feature_2",
value_a: 17,
value_b: 21,
value_x: 510
}
console.log(
Object.entries(data).reduce((a,[key,value],index)=>{
if (key==='name')
return {index, assets: {...a.assets, [value]:[]}};
return {
index : a.index,
assets : {
...a.assets,
[Object.entries(a.assets)[a.index][0]] : [...Object.entries(a.assets)[a.index][1],value]
}};
},{index:0,assets:{}}).assets
);
But we know the correct way is using separated array rows.
This can be accomplished the following way:
const obj = {
data: {
name: "feature_1",
value_a: 1,
value_b: 2
}
};
const output = {
assets: {
[obj.data.name]: Object.values(obj.data).filter(el => el !== obj.data.name)
}
}
console.log(output);

track path of recursive function

I'm trying to track path of a deep nested value in json object but having hard time getting the path. Each Item is an array of objects and can have child items. If the object c exists in the json data it is always located in the last item array.
item: [
{
a:5,
item: [
{
item: [
{c:1},
{x:4},
],
...
},
{},
{}
]
},
{},
{}
]
const findPath = (items) => {
let path = []
items.forEach((item,i) => {
if('item' in item){
path = path.concat(findPath(item.item))
}
else if('c' in item) {
path.push(i)
}
})
return path
}
if I have 3 c objects with different item depths, then I would have:
[
[0,0,0], //item[0].item[0].item[0].c
[1,0], //item[1].item[0].c
[4]] , //item[4].c
Any help?
Your main problem here is that you don't track the common case. You store the index only when you found a leaf, but you want all the steps in between. This being recursion, you also have to carry your return values with you, or you end up stepping on them. This works:
objects = [
{},
{
item: [
{},
{},
{
a:5,
item: [
{
item: [
{c:1},
{x:4},
]
},
{},
{}
]
},
{}
]
}
]
const findPath = (items, current_path, matching_paths) => {
items.forEach((item,i) => {
if('item' in item){
current_path.push(i);
current_path = current_path.concat(
findPath(item.item, current_path, matching_paths)
);
}
else if('c' in item) {
current_path.push(i);
matching_paths.push( current_path.slice() );
current_path = [];
}
})
}
var path = [];
var paths = [];
findPath(objects, path, paths);
console.log(paths); //[[1, 2, 0, 0]]
If C is found push a path object to the path array and update that path object for the rest of the paths.
const findPath = (items) => {
let path = []
items.forEach((item,i) => {
if('item' in item){
let item_path = findPath(item.item)
if(item_path.length > 0){
item_path[0].path.push(i)
path.push(item_path[0])
}
}
else if('c' in item){
path.push({path:[i], c:item.c})
}
})
return path
}
The function must be recursive, which means it should call itself with different parameters and not loop forever.
Below is what you are looking for. I made it in TypeScript to make sure I typed it correctly, but just take off all type definitions and it becomes JavaScript:
const trackPath: number[][] = [];
function findPath(topItem: any, path: number[], position: number): void
{
const currentPath = path.slice();
currentPath.push(position);
const newTopItem = topItem['item'];
if (Array.isArray(newTopItem)) {
// here is the recursion for each subitem
newTopItem.forEach((item, i) => findPath(item, currentPath, i));
}
if ('c' in topItem) {
trackPath.push(currentPath);
}
}
// this is the main method to call
function actuallyGetThePath(myTopItem: any): number[][] {
findPath(myTopItem, [], 0);
return trackPath;
}
Good luck!

Replace whole object in react state

Is there a way to update the state with a state structure like this
this.state = {
structure: [
{
selected: false,
name: "a",
key: "a",
}, {
selected: false,
name: "b",
key: "b"
}, {
selected: false,
name: "c",
key: "c",
}, {
selected: false,
name: "d",
key: "d"
}
]
}
I want to update the state. I am doing it this way:
_onPress = (obj, index) => {
const oldStateSelected = obj.selected;
const newStateObject = Object.assign({}, obj);
newStateObject.selected = !oldStateSelected;
const oldState = _.cloneDeep([...this.state.structure]);
oldState.splice(index, 1);
const newState = oldState.push(newStateObject)
this.setState({
structure: [newState]
});
}
However, that returns me a new state of
{ structure: [4] }
I think the problem is, that I am modifiing the state in place instead of replacing it?!
When I console.log(oldState) after removing the element from the array, I see that it says oldState (3) [Object, Object, Object].
But when I open it, there are 4 array elements. The element I wanted to remove with splice is still in there.
Any ideas?
Problem is in this line:
const newState = oldState.push(newStateObject);
array.push will not return the updated array, when we use push it will update the original array, so you need to assign the variable oldState value to state variable structure.
Use this:
_onPress = (obj, index) => {
const oldStateSelected = obj.selected;
const newStateObject = Object.assign({}, obj);
newStateObject.selected = !oldStateSelected;
const oldState = _.cloneDeep([...this.state.structure]);
oldState.splice(index, 1);
oldState.push(newStateObject)
this.setState({
structure: oldState //here
});
}
Check this snippet:
let a = [10,15,20];
let b = a.push(20);
console.log('a = ', a);
console.log('b = ', b);
Array.prototype.push is not returning the array. Just push and do
this.setState({
structure: oldState
});

Categories

Resources