Why are these methods disappearing when I clone the array? [duplicate] - javascript

This question already has answers here:
What is the most efficient way to deep clone an object in JavaScript?
(67 answers)
Closed 4 years ago.
For some strange reason, when this data:
// data
visitorsTemplate: [{
text: '',
type: 'buttons',
children: [{
name: 'email',
method: (e) => { this.sendEmail(e) }
}]
}]
Is cloned:
// watch
console.log(this.visitorsTemplate)
const visitorItem = clone(this.visitorsTemplate)
console.log(visitorItem)
With this function:
// utils
export const clone = (...args) => {
return JSON.parse(JSON.stringify.apply(null, args))
}
the method attribute disappears. Here are the console.logs:
[{
text: "",
type: "buttons",
children": [{
name: "email",
method: f method(e)
}, {
name: "delete",
method: f method(e)
}]
}]
[{
text: "",
type: "buttons",
children": [{
name: "email"
}, {
name: "delete"
}]
}]
Update: I found out JSON.stringify is removing the methods but I need to create a new array. So how to avoid removing the methods?

You can implement your own deep clone implmentaion of object. Try this code.
function deepcloneObject(obj) {
var clone = {};
for(var i in obj) {
if(obj[i] != null && typeof(obj[i])=="object")
clone[i] = deepcloneObject(obj[i]);
else
clone[i] = obj[i];
}
return clone;
}

If you check the JSON Specs here , you will notice that there is no specification for a JSON to contain methods and it contains only data. When you do stringify and then parse, you are taking intermediate step as JSON which is causing this.
For a more detailed account on cloning array of objects, please refer this post.
Hope this helps!

the moment you do JSON.stringify, they will try to create a string of your JSON object. when you have method inside, it will try to convert to string also.
So, if you want to have a new instance of object you can use this: http://underscorejs.org/#clone
newObject = _.clone(visitor)
or if you want to be able to create programatically later, you prepare these:
function Visitor(){
return {
text: '',
type: 'buttons',
children: [Child()]
}
}
function Child(){
return {
name: 'email',
method: (e) => { this.sendEmail(e) }
}
}

Use Object.assign along with Array.prototype.map
const clonedArray = array.map(a => Object.assign({}, a));

Related

JS Using objects dinamic keynames

I need to use object which contains my settings, mainly keynames assignment. But I cant figure out why it does not work
//This is my object which contains names of the keys of another object
let setup={
param1:'data1',
param2: 'data2'
}
//So here is the main object where I need to use values as a keynames
const StatDataObj = {
DataFields: {
['setup.param1']: {Blocks: [],Patch: []},
['setup.param1']: {Blocks: [],Patch: []}
}
}
Everything seems quite simple but it gives me error! So what im doing wrong?
Try this:
const setup = { param1:'data1', param2: 'data2' };
const StatDataObj = {
DataFields: {
[setup.param1]: { Blocks: [], Patch: [] },
[setup.param2]: { Blocks: [], Patch: [] }
}
};
console.log(StatDataObj);
The problem is that you adding string, not variable value
//This is my object which contains names of the keys of another object
let setup={
param1:'data1',
param2: 'data2'
}
//So here is the main object where I need to use values as a keynames
const StatDataObj = {
DataFields: {
[setup.param1]: {Blocks: [],Patch: []},
[setup.param1]: {Blocks: [],Patch: []}
}
}
console.log(StatDataObj)

How to copy a variable arrray from another arrray in javascripts

I have an array as below:
const arr = [
{
title: 's4',
value: '124'
},
{
title: 's2',
value: '121'
},
{
title: 's3',
value: '122'
}
];
and I want to create a new another array copy from the old array same as below:
const arrCopy = [
{
value: '124'
},
{
value: '121'
},
{
value: '122'
}
];
then my code as below:
var arrCopy = [...arr,arr.value]
but it has a problem, so anyone help me, thanks.
Just as in the comment above you can use awesome Javascript functions, in this case, you would like to use the map function of your array to map every item of the array as you like.
const arrayMapped = yourArray.map(item => {
value: item.value
})
Here is another way using Javascript Destructuring, you just ask with properties would you like from the JS Object, in this case, you just like the value property.
const arrayMapped = yourArray.map(( { value } ) => ( { value } ))
How Array.map works
How Object Destructuring works
You can simply use Array.map, as it returns a new array with the required value.
const newArr = arr.map(element => ({ value: element.value }))
console.log(newArr);
For references : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
If you are allowed to import a library. Ramda has a lot of functions to work with arrays.
For your specific question, project would do the job.
import R from "ramda";
R.project(["value"], arr) //return equals arrCopy

Removing objects from array based on two properties

I have an array that contains custom objects that look like this:
{
field: fieldName,
dataType: usuallyAString,
title: titleForLocalization,
environmentLabel: environmentName
}
There are a couple of other properties on the object, but the only ones that I actually care about are field and environmentLabel. I need to filter out any objects that have identical field and environmentLabel but don't care about any other properties. The array can have objects that share field or environmentLabel, just not both.
Ideally I'd like to use Array.filter but have yet to figure out how to do it based on two properties. Also, I am limited to es5.
Create another object that contains all the combinations of properties you want to test. Use filter() and test whether the pair already exists in the object. If not, add the properties to the other object and return true.
var seen = {};
newArray = array.filter(function(obj) {
if (seen[obj.field]) {
if (seen[obj.field].includes(obj.environmentLabel) {
return false;
} else {
seen[obj.field].push(obj.environmentLabel);
}
} else {
seen[obj.field] = [obj.environmentLabel];
}
return true;
});
const data = [{
field: 1,
dataType: "usuallyAString",
title: "titleForLocalization",
environmentLabel: 1
},
{
field: 1,
dataType: "usuallyAString",
title: "titleForLocalization",
environmentLabel: 1
},
{
field: 2,
dataType: "usuallyAString",
title: "titleForLocalization",
environmentLabel: 2
}]
var result = _.uniqWith(data, function(arrVal, othVal) {
return arrVal.field=== othVal.field && arrVal.environmentLabel=== othVal.environmentLabel;
});
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
If you are able to use lodash, you can do:
var result = _.uniqWith(data, function(arrVal, othVal) {
return arrVal.field=== othVal.field && arrVal.environmentLabel=== othVal.environmentLabel;
});
console.log(result)

how do I add an item on array of Objects on setState in React?

I am creating arrays of objects and storing it on variables like this:
const nameOption = nameOptions(listHotels);
const estadoOption = stateOptions(listHotels);
const cityOption = cityOptions(listHotels);
my state is currently like this:
selectFilter: [
{ id: 1, type: 'Name'},
{ id: 1, type: 'Estado'},
{ id: 1, type: 'Cidade'},
],
I want to add these variables in a property called "options", like so:
selectFilter: [
{ id: 1, type: 'Name', options: nameOption},
{ id: 1, type: 'Estado', options: estadoOption},
{ id: 1, type: 'Cidade', options: cityOption},
],
how do I do it using the immutable react way?
First store the values to be inserted into a plain object, where the properties match the type values of selectFilter :
const options = {
Name: nameOptions(listHotels),
Estado: stateOptions(listHotels),
Cidade: cityOptions(listHotels)
}
Then merge that with selectFilter into the extended version of it:
this.setState(prevState => ({
selectFilter: prevState.selectFilter.map(filter =>
({...filter, options: options[filter.type]})
)
}));
NB: there is probably a more efficient way to build the options object, since you seem to iterate listHotels for each property. This could probably be done in one sweep using reduce. But without details about what these functions (nameOptions, stateOptions, ...) do there is not much I can offer for that. Look into calling listHotels.reduce.
You can use Object.assign() to duplicate your state into an new object. This object is now mutable. Once done modifying it, you then replace your entire state with the new version. Here is an example of how that could be done.
handleChange= () => {
let mutableState = Object.assign({}, this.state);
mutableState.thingToChange = foo;
this.setState(mutableState);
};
this.setState(({selectFilter}) => (
[
{...selectFilter[0], options: nameOptions},
{...selectFilter[1], options: estadoOptions},
{...selectFilter[2], options: cityOptions},
]
);

replacing an object of a collection using spread operator

I am trying to return a new columns object with updated tabs array and replace this object the existing one in my datasource. Here's the relevant part. Below, uses spread, however, it adds my object to the end of the columns. How can I make it replace the existing column?
Thanks!
newState = {
columns: [
{ id: column.id, title: column.title, tabs: removedTabs },
...state.columns
],
columnOrder: ["chromeTabs", ...state.columnOrder]
};
codesandbox link
newState = {
columns: [
...state.columns.filter(item => item.id !== column.id),
{ id: column.id, title: column.title, tabs: removedTabs }
],
columnOrder: [...state.columnOrder.filter(item => item !== 'chromeTabs'), "chromeTabs"]
};
return a filtred array and your new item, should do what you expect, using spread operator to replace existing item only work on object (because key are unique) not array.
const obj = {
cool: "is it cool ?"
};
console.log({ ...obj, cool: "definetly" });

Categories

Resources