Copy out a child Object in JavaScript - javascript

I'm trying to copy out a child Object into a variable, but it seems that by simply declaring it, only the key get's copied. How do I copy out the entire Object? Here's what I'm trying...
const baseObj = {
players: {
player1: {
name: "hello",
details: "something"
},
player2: ...
}
}
const player1Copy = baseObj.players.player1
// I want to grab out the whole object player1 instead of just the key

use Object.assign
DEMO
const baseObj = {
players: {
player1: {
name: "hello",
details: "something"
}
}
}
let cloned = Object.assign({}, baseObj.players.player1);
console.log(cloned);

Related

Merging values from an array of strings into a nested object in javascript

I want to merge values from an array into a static nested object. The array containing the values is something like this,
['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27']
and the object in which the values has to be merged is,
const person = {
details_1: {
name: null,
hobbies: null,
profession: null
},
details_2: {
age: null
}
};
I want my output object to look like below,
const updated_person = {
details_1: {
name: 'ABC XYZ',
hobbies: [M,N,O,P],
profession: 'S'
},
details_2: {
age: 27
}
};
Thanks a lot for your help!
I made another solution with a different approach.
Here I used an interface weher I described the desired data structure.
In the second part the string array is tranformed into key and value pairs. Thereform are filtered the keys of interface and added into an empty object literal.
const data = ["name=ABC XYZ", "hobbies=[M,N,O,P]", "profession=S", "age=27"];
const dataInterface = {
details_1: { name: null, hobbies: null, profession: null },
details_2: { age: null },
};
function orederData(arr) {
const record = arr.map((item) => {
let [key, value] = item.split("=");
if (value[0] === "[" && value[value.length - 1] === "]") {
value = value.slice(1, value.length - 1).split(",");
}
return { key, value };
});
const dataBlock = {};
Object.keys(dataInterface).map((detail) => {
dataBlock[detail] = {};
Object.keys(dataInterface[detail]).forEach((dataKey) => {
dataBlock[detail][dataKey] = record.filter((record) => {
return record.key === dataKey;
})[0].value;
});
});
return dataBlock;
}
const orderedData = orederData(data);
console.log(orderedData);
You can simply achieve this by iterating the input array.
const arr = ['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27'];
const person = {
details_1: {},
details_2: {}
};
arr.forEach(item => {
(item.split('=')[0] !== 'age') ? person.details_1[item.split('=')[0]] = item.split('=')[1] : person.details_2[item.split('=')[0]] = item.split('=')[1]
});
console.log(person);
There is no way to cleanly merge an unstructured array into a structured object such that the array values end up in the appropriately keyed person properties.
javascript does provide the assign() function that merges objects but for YOUR requirements your source data needs to be an object similarly structured and not an array.
so this:
['name=ABC XYZ', 'hobbies=[M,N,O,P]', 'profession=S', 'age=27']
would need to become this:
const source= [{details_1: {"name":"ABC XYZ", "hobbies":"[M,N,O,P]", "profession":"S"}, details_2: {"age":"27"}}]
such that a call to Object.assign():
const new_person = Object.assign(person, source[0]);
fills this
const person = {
details_1: {
name: null,
hobbies: null,
profession: null
},
details_2: {
age: null
}
};
properly, though you may need to clone or instantiate and empty person first.
or, if person is an Object you could have a fill() method that knows what to do with the array data.

Get value of parent Object

Is there any way to get a key-value from an object's parent object? In the example below, I want to combine urlParent with section:
const linkItems = [
{
id: 1,
name: 'Home Page',
urlParent: '/home',
subItems: [
{
subId: 1,
name: 'Project 1',
section: '#project1',
get url() {
//output /home#project1
}
}
]
}
];
console.log(linkItems[0].subItems[0].url) // /home#project1;
You cannot reference an Object parent that way (Object(value) ← Array ← Object), and not even from an Object's parent Object.
What you can do instead is:
Create two Classes, one for the parent and one for the child.
When adding a child to the parent, just make a "linked list", by referencing the parent's this to the created child item parent property
class Child {
constructor(data) {
Object.assign(this, data);
}
get url() {
return this.parent.urlParent + this.section
}
}
class Parent {
constructor(data) {
Object.assign(this, data);
this.subItems = [];
}
addChild(item) {
this.subItems.push(new Child({...item, parent: this}));
}
}
// Example:
const parent = new Parent({id:1, name:"Home", urlParent:"/home"});
parent.addChild({subId:1, name:"Project 1", section:"#project1"});
console.log(parent.subItems[0].url) // /home#project1;
But hey! Nodes and trees
Your original idea and the above use too much complexity.
What I'd suggest is to treat all parent, child, whatever, as Page Nodes.
class Page {
constructor(data) {
Object.assign(this, data);
this.children = {};
}
addChild(page) {
page.parent = this; // Linked to parent!
this.children[page.id] = page;
}
get url() {
// Generate full URI by recursing the parents tree
return this.parent ? `${this.parent.url}/${this.slug}` : this.slug;
}
}
// Example:
// 1. Create pages:
const pageRoot = new Page({id:1, name:"Home page", slug:""});
const pageProj = new Page({id:3, name:"All projects", slug:"projects"});
const pageWebs = new Page({id:4, name:"Websites", slug:"websites"});
const pageStOv = new Page({id:6, name:"Stack Overflow", slug:"so"});
const pageSpec = new Page({id:9, name:"Stack Overflow Specs", slug:"specs"});
// 2. Create Tree:
pageRoot.addChild(pageProj);
pageProj.addChild(pageWebs);
pageWebs.addChild(pageStOv);
pageStOv.addChild(pageSpec);
// 3. Test
console.log(pageRoot.url); // "";
console.log(pageProj.url); // "/projects";
console.log(pageSpec.url); // "/projects/websites/so/specs";
console.log(pageRoot);
const linkItems = [
{
id: 1,
name: 'Home Page',
urlParent: '/home',
get subItems(){
console.log(this.name);
return ([
(() => {
console.log(this);
return {
subId: 1,
name: 'Project 1',
section: '#project1',
urlParentFromOuterScope: () => {
return this.urlParent;
},
sectionString(){
return this.section;
},
url(){
console.log('url', this);
return this.urlParentFromOuterScope() + this.sectionString();
}
}
})()
])
}
}
];
const subItems = linkItems[0].subitems;
console.log(linkItems[0].subItems[0].url());
Please feel free to remove the unnecessary 'console.log's after you understand the approach.
I took the liberty of adding a few methods.
This is a tricky one and has to do with the scope of this in array functions.
P.S.: I guess this can be simplified.

How to assigning Object into another object (not merging)

var student={
student1:{
name:"John",
}
}
var student2={
name:"Doe"
}
Resultamt object should contain both student object with their object's name
Like this :-
students={
student1={name:"John"},
student2={name:"Doe"}
}
You can use object spread to solve this.
e.g
let student = {
student1: {
name: "John",
},
};
let student2 = {
name: "Doe",
};
let students = {
...student,
student2,
};
console.log(students);
Using spread operation you can combine them.
const students = {
...student1,
...student2
};

Multi level dynamic key setting?

How do you set a multi-level deep update on an object with a dynamic key in javascript? When i try a typical dynamic update, it adds another key/value pair instead of updating the correct parameter.
let home = {
street: {
house: {
room: {
window: true
}
}
}
}
let update = {
key: "house.room.window",
value: "false"
}
home.street[update.key] = update.value;
console.log(home);
expected:
home = {
street:{
house: {
room: {
window: false
}
}
}
}
instead i get:
home = {
street:{
house: {
room: {
window: true
}
}
"house.room.window": false
}
}
Try like below. Explanation is in comments.
let home = {
street: {
house: {
room: {
window: true
}
}
}
}
let update = {
key: "house.room.window",
value: "false"
}
// select object to get updated
let obj = home.street;
// get array of nested keys
let nestedKeys = update.key.split('.');
// get object from nested keys until last key
// used slice(0, -1) so it will iterate through all key except last one
nestedKeys.slice(0, -1).forEach(k => obj = obj[k]);
// use object with last key and update value
obj[nestedKeys[nestedKeys.length - 1]] = update.value
// log object
console.log(home);
If you don't mind using a library, you can try lodash's update.
_.update(home, update.key, () => update.value);

How to access other object sibling's value?

I'm just wondering if it's possible to refer to self (object) value inside the object sibling like below?
[
{
"name": "Zulh",
"name_uppercase": uppercase(self.name) // expects ZULH
},
{
"name": "John",
"name_uppercase": uppercase(self.name) // expects JOHN
}
]
Note:
Code for uppercase is omitted for brevity. In my real code, it's doing synchronous complex stuff and is not actually simple string case manipulation like that.
Using a GETTER
If you want to keep it dynamic and make it work even if you change the name property, you can use a GETTER to do this kind of thing:
const names = [
{
"name": "John",
get name_uppercase() {
return this.name.toUpperCase();
}
}
]
console.log(names[0].name_uppercase)
GETTER for multiple objects
You don't have to write this for every property manually! Use .forEach:
const names = [
{
"name": "John"
},
{
"name": "Mike"
}
]
names.forEach(object => {
Object.defineProperty(object, 'nameUppercase', {
get: function() { return this.name.toUpperCase() }
});
});
console.log(names[0].nameUppercase)
console.log(names[1].nameUppercase)
Using a class and a GETTER
Or as #Rajesh pointed out you can use a class instead:
class Person {
constructor(name) {
this.name = name;
}
get nameUpperCase() {
return this.name.toUpperCase();
}
}
const names = [ new Person("John"), new Person("Mike")];
console.log(names[0].nameUpperCase);
console.log(names[1].nameUpperCase);
You can't reference an object during initialization when using object literal syntax.. Inshort, that's not possible what you expect above
Well, you can use map and add additional/modified properties to you object like
data.map(o=> ({name: o.name, upper_case : o.name.toUpperCase()}))
var data = [
{
"name": "Zulh"
},
{
"name": "John"
}
];
var x = data.map(o=> ({name: o.name, upper_case : o.name.toUpperCase()}))
console.log(x)
You can use Array.forEach and update the objects in Array
var data = [{"name": "Zulh"},{"name": "John"}];
data.forEach(o=> o.upper_case = o.name.toUpperCase());
console.log(data);
Why not create a function that transforms your incoming array? A way to do it could be like this:
const value = [
{
"name": "Zulh"
},
{
"name": "John"
}
];
const transform = ( array, propertyToUpdate, propertyToCreate, transformation ) => {
return array.map( item => ({ ...item, [propertyToCreate]: transformation( item[propertyToUpdate] ) }) );
};
console.log( transform( value, 'name', 'name_uppercase', ( item ) => item.toUpperCase() ) );
You can't do this with the object literal syntax, since it's 'this' property will not be set at that time. For example, if you'd run your code in the browser, 'this' would refer to the window object.
So you'll either have to use one of the other answers or go for a 'class':
var uppercase = function( str ) {
return str.toUpperCase();
};
var Person = function( name ) {
this.name = name;
this.name_uppercase = uppercase( this.name );
};
var persons = [
new Person( 'zuhi' ),
new Person( 'john' )
];
console.log( persons );
Same can be written in ES6 class syntax.
I would suggest 2 approaches:
If you DO NOT want to change your initial array ( which is recommended ), use map which returns a new array with changed values ( calls a function for every array item ) .
See below
let arr = [
{
"name": "Zulh",
},
{
"name": "John",
}
];
const newArr = arr.map((x)=>{
x.name_uppercase = (x.name).toUpperCase()
return x
})
console.log(newArr)
If you don't mind changing your initial array, you can use forEach. Keep in mind that unlike map, forEach changes your array and so it doesn't return anything.
let arr = [
{
"name": "Zulh",
},
{
"name": "John",
}
];
arr.forEach((x)=>{
x.name_uppercase = (x.name).toUpperCase()
})
console.log(arr)
So it all depends if you want to change your current array or not
How about using a getter method?
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/get
The get syntax binds an object property to a function that will be
called when that property is looked up.
foo = [
{
"name": "Zulh",
get name_uppercase () {
return (this.name).toUpperCase();
}
},
{
"name": "John",
get name_uppercase () {
return (this.name).toUpperCase();
}
}
]
console.log(foo[1].name_uppercase); //returns JOHN
Hope it helps :)

Categories

Resources