How can I update state while preserving immutability? - javascript

I'm not using the immutable library and I'm working on it.
I want to update the key and value values ​​of mapTable without changing the constant of PRICE_OPTION2_STATE.
I'd appreciate it if you could tell me how.
I have posted the results of the problem and the results I want.
//my code
tableData['option2Price'] = 50000;
const mapCopy = { ...copyState };
let tableCopy = { ...mapCopy[currentTableIndex].mapTable[findTableIndex] };
tableCopy = tableData;
mapCopy.tableCopy = tableCopy;
// problem
console.log(PRICE_OPTION2_STATE);
// initstate
const PRICE_OPTION2_STATE = [
{
id: 1,
option2Name: '',
option2LeftSelect: 'sell',
mapTable: [
{
tableId: 1,
flag: true,
option2Value: '',
option2Price: '',
discountInput2: '',
discountOption2: 'won',
option2Payment: '',
option2Tax: '',
option2settlementAmount: '',
option2RightSelect: 'sell',
},
],
},
];
PRICE_OPTION2_STATE => problem console result
[
{
id: 1,
option2Name: '',
option2LeftSelect: 'sell',
mapTable: [
{
tableId: 1,
flag: true,
option2Value: '',
option2Price: '',
discountInput2: '',
discountOption2: 'won',
option2Payment: '50000',
option2Tax: '',
option2settlementAmount: '',
option2RightSelect: 'sell',
},
],
},
]
result i want
[
{
id: 1,
option2Name: '',
option2LeftSelect: 'sell',
mapTable: [
{
tableId: 1,
flag: true,
option2Value: '',
option2Price: '',
discountInput2: '',
discountOption2: 'won',
option2Payment: '',
option2Tax: '',
option2settlementAmount: '',
option2RightSelect: 'sell',
},
],
},
]

I'll assume copyState is a reference to PRICE_OPTION2_STATE or at least is some sort of (shallow) copy of it. So to copy it, you should:
not use object spread notation at the top level, since it is an array.
not leave it at a shallow copy, but copy it deeply. So you'll also need to map the inner array and copy the objects it has.
Here is some inspiration:
// Assuming copyState has the structure of PRICE_OPTION2_STATE
// Get a deep copy
const mapCopy = copyState.map(({mapTable, ...rest}) => ({
...rest,
mapTable: mapTable.map(obj => ({...obj}))
}));
// Now this assignment will not impact copyState / PRICE_OPTION2_STATE
mapCopy[currentTableIndex].mapTable[findTableIndex] = tableCopy;

Related

JavaScript: How to update the values of a nested array (linter throwing 'no-param-reassign' warning)

I have an array that looks like this:
const MENUS_LIST: MenuListing[] = [
{
id: 1,
name: 'Analytics',
defaultPath: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview', '/analytics/sales/bookings'],
submenu: [
{
label: 'Overview',
path: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview'],
additionalIcon: [],
name: ['Overview'],
id: 'sales-overview',
},
{
label: 'Bookings',
path: '/analytics/sales/bookings',
relatedPath: ['/analytics/sales/bookings'],
additionalIcon: [],
name: ['Bookings'],
id: 'sales-bookings',
},
],
},
];
I need to convert it to the following format - by adding the isActive flag to the main structure and submenu when the current path === submenu.path.
In the following example, we assume path to be /analytics/sales/overview.
[
{
id: 1,
name: 'Analytics',
defaultPath: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview', '/analytics/sales/bookings'],
isActive: true,
submenu: [
{
label: 'Overview',
path: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview'],
additionalIcon: [],
name: ['Overview'],
id: 'sales-overview',
isActive: true,
},
{
label: 'Bookings',
path: '/analytics/sales/bookings',
relatedPath: ['/analytics/sales/bookings'],
additionalIcon: [],
name: ['Bookings'],
id: 'sales-bookings',
isActive: false,
},
],
},
];
I have the following solution which works (code is simplified):
menuX = (MENUS_LIST as MenuListingProps[]).map((m: MenuListingProps) => {
const resultX = { ...m };
resultX.isActive = true; // will perform checking to determine true or false
(m.submenu as MenuItemProps[]).forEach((sm: MenuItemProps) => {
sm.isActive = true; // linter warning; value assigned based on checking (can be true or false)
sm.icon = 'abc'; // linter warning
sm.title = 'xyz'; // linter warning
});
return resultX;
});
But the linter is complaining of Assignment to property of function parameter "sm" on the lines where I'm assigning values to sm
Based on this SO post, I understand that I need to copy the argument to a temporary variable and work on it instead.
I did this by creating a new var resultX. But I'm not sure how to go about doing the same with sm.
Seeking some guidance, thank you.
menuX = (MENUS_LIST as MenuListingProps[]).map((m: MenuListingProps) => {
const resultX = { ...m };
resultX.isActive = true; // will perform checking to determine true or false
resultX.submenu = (m.submenu as MenuItemProps[]).map((sm: MenuItemProps) => {
const sub = {...sm};
sub.isActive = true; // linter warning; value assigned based on checking (can be true or false)
sub.icon = 'abc'; // linter warning
sub.title = 'xyz'; // linter warning
return sub;
});
return resultX;
});
Here's a method using Object.assign.
Object.assign doesn't mutate the original object, so it returns a new object with the given changes.
const MENUS_LIST = [{"id":1,"name":"Analytics","defaultPath":"/analytics/sales/overview","relatedPath":["/analytics/sales/overview","/analytics/sales/bookings"],"submenu":[{"label":"Overview","path":"/analytics/sales/overview","relatedPath":["/analytics/sales/overview"],"additionalIcon":[],"name":["Overview"],"id":"sales-overview"},{"label":"Bookings","path":"/analytics/sales/bookings","relatedPath":["/analytics/sales/bookings"],"additionalIcon":[],"name":["Bookings"],"id":"sales-bookings"}]}];
const menuX = MENUS_LIST.map(menu => Object.assign(menu, {
isActive: true, // will perform checking to determine true or false
submenu: menu.submenu.map(submenu => Object.assign(submenu, {
isActive: true, // will perform checking to determine true or false
icon: 'abc', // linter warning
title: 'xyz' // linter warning
}))
}));
console.log(menuX);
I hope this code helping you
var array = [
{
id: 1,
name: 'Analytics',
defaultPath: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview', '/analytics/sales/bookings'],
isActive: true,
submenu: [
{
label: 'Overview',
path: '/analytics/sales/overview',
relatedPath: ['/analytics/sales/overview'],
additionalIcon: [],
name: ['Overview'],
id: 'sales-overview',
isActive: true,
},
{
label: 'Bookings',
path: '/analytics/sales/bookings',
relatedPath: ['/analytics/sales/bookings'],
additionalIcon: [],
name: ['Bookings'],
id: 'sales-bookings',
isActive: false,
},
],
},
];
array.map(o=>o.submenu.map(v=> o.defaultPath == v.path? {...v,isActive:true,icon:"abc",title:'xyz'}:{...v,isActive:false,icon:"abc",title:'xyz'}))

How can i get data from a JSON array in google app script?

I'm doing a request to an API that is successful but i need to get the data of the array returned, below i will put how the array looks like so you can help me to extract the data
{ total_grand: 30600000,
total_billable: null,
total_currencies: [ { currency: null, amount: null } ],
total_count: 5,
per_page: 50,
data:
[ { id: 13998122,
pid: 1570982183,
tid: null,
uid: 5386231,
description: 'Finish the first part of the RCP mockup',
start: '2020-03-26T13:00:00-04:00',
end: '2020-03-26T16:00:00-04:00',
updated: '2020-04-02T13:25:15-04:00',
dur: 10800000,
user: 'Jose',
use_stop: true,
client: 'PLA',
project: 'Training',
project_color: '0',
project_hex_color: '#3750b5',
task: null,
billable: null,
is_billable: false,
cur: null,
tags: []
} ]
}
I want to access to the user,project,tags,client,start,end and description so i can put it in my SpreadSheet. How can i do that?
This is how i do the request and how i try to access to the data in the array in my variable togglData
for (var i = 0; i < projects.length; i++) {
var listProjects = projects[i];
var reportURL = baseURL + '/reports/api/v2/details' + params;
var reportFetch = UrlFetchApp.fetch(reportURL, options);
var togglReport = JSON.parse(reportFetch.getContentText());
var togglData = togglReport["data"]["user"];
Logger.log(togglReport);
}
Range.setValues() is used to the set data as a two dimensional array to the sheet. Using destructuring assignment and for...of loop, It's possible to mould the data to a 2D array.
const togglReport = {
total_grand: 30600000,
total_billable: null,
total_currencies: [{ currency: null, amount: null }],
total_count: 5,
per_page: 50,
data: [
{
id: 13998122,
pid: 1570982183,
tid: null,
uid: 5386231,
description: 'Finish the first part of the RCP mockup',
start: '2020-03-26T13:00:00-04:00',
end: '2020-03-26T16:00:00-04:00',
updated: '2020-04-02T13:25:15-04:00',
dur: 10800000,
user: 'Jose',
use_stop: true,
client: 'PLA',
project: 'Training',
project_color: '0',
project_hex_color: '#3750b5',
task: null,
billable: null,
is_billable: false,
cur: null,
tags: [],
},
],
};
const out = [];
for (const {
user,
project,
tags,
client,
start,
end,
description,
} of togglReport.data) {
//We're looping over togglReport.data and not togglReport
out.push([user, project, tags.join(), client, start, end, description]);
}
console.log(out);
//SpreadsheetApp.getActive().getSheets[0].getRange(1,1, out.length, out[0].length).setValues(out);

Refactor JS array in the specific format the backend is expecting

I have this array that's returned when a user submits a form:
leaders: [
{
email: 'name#domain.io',
sites: [
{
name: 'Test',
sitemeta_id: 'xxxxxxxxx',
_checked: true
},
{
name: 'Test 2',
sitemeta_id: 'xxxxxxxx',
_checked: true
}
],
focused: false,
_sitesChecked: 1
},
{
email: 'dog#gmail.com',
sites: [
{
name: 'Some Name',
sitemeta_id: 'xxxxx',
_checked: true
},
{
name: 'Names',
sitemeta_id: 'xxxxxxxx'
}
],
focused: false,
_sitesChecked: 2
}
]
I'd like to refactor this to send this array in the specific format the backend is expecting, which would only include site names with the value _checked as true, so the site "Names" wouldn't be included, for example:
leaders: [
{
email: 'name#domain.io',
sites: ['Test', 'Test2']
},
{
email: 'dog#gmail.com',
sites: ['Some Name']
}
]
What's the best way to achieve this in JS (or AngularJS)?
You can use .map to transform one array into another, and destructure the arguments to reduce syntax noise:
const leaders = [{email:'name#domain.io',sites:[{name:'Test',sitemeta_id:'xxxxxxxxx',_checked:!0},{name:'Test 2',sitemeta_id:'xxxxxxxx'}],focused:!1,_sitesChecked:1},{email:'dog#gmail.com',sites:[{name:'Some Name',sitemeta_id:'xxxxx',_checked:!0},{name:'Names',sitemeta_id:'xxxxxxxx',_checked:!0}],focused:!1,_sitesChecked:2}];
const output = leaders.map(
({ email, sites }) => ({
email,
sites: sites.reduce(
(accum, { name, _checked }) => _checked ? [...accum, name] : accum,
[]
)
})
);
console.log(output);

Map and Filter to populate array with objects by id

I need to populate array of ids with objects. In other words I have. Array of ids:
var orderArray = ["5ace454a2b22e17597d0e694", "5acde7c0f7d2520e3b205971", "5ad2086bf05ad342dc723ea1"]
And array of objects:
var objectsArray = [ { _id: 5acde7c0f7d2520e3b205971,
name: 'Dinner',
restaurant: 5a68d8ea17d9e4308e6400c3,
created: 2018-04-11T10:47:28.957Z,
status: true,
products: [ [Object] ] },
{ _id: 5ace454a2b22e17597d0e694,
name: 'Test',
restaurant: 5a68d8ea17d9e4308e6400c3,
image:
{ _id: 5ad23ed177bcd07303f62899,
filename: 'rJKCR2k2f-1523728081111.jpeg',
destination: 'images',
binded: true },
created: 2018-04-11T17:26:34.186Z,
status: false,
products: [ [Object], [Object] ] },
{ _id: 5ad2086bf05ad342dc723ea1,
name: 'Test',
restaurant: 5a68d8ea17d9e4308e6400c3,
image: null,
created: 2018-04-14T13:55:55.449Z,
status: true,
products: [] } ]
Either you can sort array of objects based on ids... Or map array of ids to array of objects. Probably I'd prefer the second option.
But my approach just doesn't work
orderArray.map(id => objectsArray.filter(obj => obj._id == id))
The result shall be: objectsArray is sorted as order of elements in orderArray
SOLUTION: I've opened this question few days ago: Merging 2 arrays with different value types
Here I have the same problem. orderArray is array of objects (not string) thus in order to make it work I need to apply the solution I found earlier (both Array.filter and Array.find functions works well):
but in my way it will work only if:
order_array.map(String).map(e => objectsArray.find(a => a._id == e))
//as well as
order_array.map(String).map(e => objectsArray.filter(a => a._id == e))
map the first array to fill it with corresponding elements from the second one :
var orderArray = ["5ace454a2b22e17597d0e694", "5acde7c0f7d2520e3b205971", "5ad2086bf05ad342dc723ea1"]
var objectsArray = [ { _id: '5acde7c0f7d2520e3b205971',
name: 'Dinner',
restaurant: '5a68d8ea17d9e4308e6400c3',
created: '2018-04-11T10:47:28.957Z',
status: true,
products: [ [Object] ] },
{ _id: '5ace454a2b22e17597d0e694',
name: 'Test',
restaurant: '5a68d8ea17d9e4308e6400c3',
image:
{ _id: '5ad23ed177bcd07303f62899',
filename: 'rJKCR2k2f-1523728081111.jpeg',
destination: 'images',
binded: true },
created: '2018-04-11T17:26:34.186Z',
status: false,
products: [ [Object], [Object] ] },
{ _id: '5ad2086bf05ad342dc723ea1',
name: 'Test',
restaurant: '5a68d8ea17d9e4308e6400c3',
image: null,
created: '2018-04-14T13:55:55.449Z',
status: true,
products: [] } ]
var sorted = orderArray.map((e) => { return objectsArray.find((a) => { return a._id == e})})
console.log(sorted)
You should be able to:
objectsArray.filter(obj => ordersArray.includes(obj._id));
If I am understanding correctly.
Using map/find (instead of filter):
let mappedArray = orderArray.map(id => objectsArray.find(obj => obj._id == id));
which maps orderArray to an array of objects, where it finds the object from objectsArray that has the same _id as the current id.
Note: If there is no object in objectsArray that matches the id, null will be returned.

updating value of array of object using lodash

My state object is:
[
{
traveller1_dob: '',
traveller1_firstName:'',
traveller1_isPreviousTraveller: false,
traveller1_surname:'',
traveller1_title: ''
},
{
traveller2_dob: '',
traveller2_firstName:'',
traveller2_isPreviousTraveller: false,
traveller2_surname:'',
traveller2_title: ''
}
]
and my payload is:
{key: "traveller1_firstName", value: "ABC", index: 0}
key is the property of the object that needs to be updated
value: is the value we want to update
index: is the index of the traveller in state array
At the moment this is the way I updated:
let obj = state[payload.index];
obj[payload.key] = payload.value;
return _.unionBy(state, [obj], payload.key);
I am aware its not the best way.
Output should be:
[
{
traveller1_dob: '',
traveller1_firstName:'ABC',
traveller1_isPreviousTraveller: false,
traveller1_surname:'',
traveller1_title: ''
},
{
traveller2_dob: '',
traveller2_firstName:'',
traveller2_isPreviousTraveller: false,
traveller2_surname:'',
traveller2_title: ''
}
]
Ideally I want to get rid of index if it's possible.How would you do this?
You're right, you can get rid of the index, and just map over your state and check hasOwnProperty on each stateItem and compare them to the payload.key. The snippet below should solve your problem:
let state = [{
traveller1_dob: '',
traveller1_firstName: '',
traveller1_isPreviousTraveller: false,
traveller1_surname: '',
traveller1_title: ''
}, {
traveller2_dob: '',
traveller2_firstName: '',
traveller2_isPreviousTraveller: false,
traveller2_surname: '',
traveller2_title: ''
}
];
function updateState(payload) {
const updatedState = _.map(state, stateItem => {
if (stateItem.hasOwnProperty(payload.key)) {
stateItem[payload.key] = payload.value;
}
return stateItem;
});
console.log(updatedState);
return updatedState;
}
const samplePayload = {
key: "traveller1_firstName",
value: "ABC",
index: 0
};
updateState(samplePayload);
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Categories