Update nested array using setState() - javascript

I want to setState() to edit parameter value which is in parameterData Array.
using setState method. If we can do this without any third part library like Immutabilty helper or lodash that would be great!
The Given state is
const[state,setState]= useState ([{
"id": 0,
"targets": {
"ageGender": {
"key":value
},
"parameterData": [
{
"id": 1,
"parameter": "Low",
"expression": "<",
"val": "10",
"indicator": "Select"
},
]
}
}])
I have tried this solution
where
idx = state index where object is situated index = index of
parameterData array to be changed event = event of change
const handleChangeTextParameter = (event, index, idx) => {
const { name, value } = event.target;
setState((prev) => {
const newState = prev.map((obj, index1) => {
let fil;
if (index1 === idx) {
fil = obj.targets.parameterData.map((data, indexOne) => {
if (indexOne === index) {
return { ...data, parameter: value };
}
return data;
});
const p = obj.targets.parameterData.map(
(obj) => fil.find((o) => o.id === obj.id) || obj
);
return { ...obj, targets: { ...obj.targets, parameterData: p } };
}
});
return newState;
});
};

Disclaimer: The state variables you defined are called state and setState, but in your handleChangeTextParameter you are using setTargets so I assume they are the same
I think the following should work:
setTargets((prev) => {
const newTargets = prev.map((object, objectIndex) => {
if (objectIndex !== idx) return object;
return {
...object,
targets: {
...object.targets,
parameterData: object.target.parameterData.map((data, dataIndex) => {
if (dataIndex !== index) return data;
return { ...data, parameter: value };
}),
},
};
});
return newTargets;
});

Related

ag-grid-react: getSortModel is not a function

I'm trying to get sort model from ag-grid-react component using getSortModel() but I'm getting getSortModel is not a function
my code
onSortChanged={useCallback(e => console.log(e.api.getSortModel(), 'im from sort'))}
"#ag-grid-community/react": "27.3.0",
"#ag-grid-enterprise/all-modules": "27.3.0",
After spend some time found params.api.getSortModel() is deprecated after version 24.0.0.
Using Column state for get Sort model and set Sort model in the following way
getSortModel:
const onSortChanged = useCallback(() => {
const value = gridParams.columnApi.getColumnState().find(s => s.sort != null)
if (value) {
setSortModel([ value ])
} else {
setSortModel([])
}
}, [ gridParams, setSortModel ])
setSortModel:
useEffect(() => {
if (sortModel.length > 0) {
const curretSortModel = gridParams.columnApi.getColumnState()
const mergeSortModel = curretSortModel.map(o1 => sortModel.find(o2 => o2.colId === o1.colId) || o1)
gridParams.columnApi.setColumnState(mergeSortModel)
}
}, [gridParams, sortModel]
As per this plunkr, you can retrieve and apply sort with the following example: https://plnkr.co/edit/?open=index.jsx&preview
const sortByAthleteDesc = useCallback(() => {
gridRef.current.columnApi.applyColumnState({
state: [{ colId: 'athlete', sort: 'desc' }],
defaultState: { sort: null },
});
}, []);
const saveSort = useCallback(() => {
var colState = gridRef.current.columnApi.getColumnState();
var sortState = colState
.filter(function (s) {
return s.sort != null;
})
.map(function (s) {
return { colId: s.colId, sort: s.sort, sortIndex: s.sortIndex };
});
savedSort = sortState;
console.log('saved sort', sortState);
}, []);

How to convert an array of objects into a single object using reduce?

I have the following array of object.
[
{ claimNumber1: 'R12345', checkNumber1: '' },
{ claimNumber2: 'T1234', checkNumber2: 'abcd' },
{ claimNumber3: 'Z4567', checkNumber3: 'qwer' }
]
Using reduce, I want to convert this to below.
{
claimNumber1:'R12345',
checkNumber1:'',
claimNumber2:'T1234',
checkNumber2:'',
claimNumber3:'Z4567',
checkNumber3:'',
}
I tried below but didn't get what I expected.
.reduce((obj, item) =>{
return {...obj,item}
} ,{});
You should spread the item object, because item is an object
const arr = [
{ claimNumber1: "R12345", checkNumber1: "" },
{ claimNumber2: "T1234", checkNumber2: "abcd" },
{ claimNumber3: "Z4567", checkNumber3: "qwer" },
];
const result = arr.reduce((obj, item, i) => {
return { ...obj, ...item, [`checkNumber${i + 1}`]: "" };
}, {});
console.log(result);
Almost there. You just need to spread the item as well.
.reduce((obj, item) => {
return {
...obj,
...item,
};
}, {});
I think you should spread every item in reducer.
Here is my code.
const res = arr.reduce((prev, item) => {
return { ...prev, ...item };
}, {});
And result is
{
claimNumber1: 'R12345',
checkNumber1: '',
claimNumber2: 'T1234',
checkNumber2: 'abcd',
claimNumber3: 'Z4567',
checkNumber3: 'qwer'
}
I'm not sure of the benefit of using reduce in this situation. A simple loop would be self-documenting, and easier to read.
const data = [
{ claimNumber1: 'R12345', checkNumber1: '' },
{ claimNumber2: 'T1234', checkNumber2: 'abcd' },
{ claimNumber3: 'Z4567', checkNumber3: 'qwer' }
];
const out = {};
// For every object in the array
for (const obj of data) {
// Get an array of its keys and iterate over them
for (const key of Object.keys(obj)) {
// If it's a claim add that value to the output
// object property, otherwise set that property value
// to an empty string.
if (key.startsWith('claim')) {
out[key] = obj[key];
} else {
out[key] = '';
}
}
}
console.log(out);
Additional documentation
Object.keys

Convert json into nested JavaScript Object

I have a json int file that contains any items like below:
Input JSON
{
"action.button.submit": "Submit"
"action.button.submitting": "Submitting"
"buttons.common.add": "Add"
"buttons.common.reset": "Reset"
"constants.bom.conditional.or.mandatory.conditional": "Conditional"
"constants.bom.conditional.or.mandatory.mandatory": "Mandatory"
}
Output
{
action: {
button: {
submit: 'Submit'
submitting: 'Submitting'
}
},
buttons: {
common: {
add: 'Add',
reset: 'Reset'
}
},
constants: {
bom: {
conditional: {
or: {
mandatory:{
conditional: 'Conditional',
mandatory: 'Mandatory'
}
}
}
}
}
}
This was as far as I could get:
newData = {};
Object.keys(data).forEach(item => {
const splitData = item.split('.');
splitData.forEach((detail, index) => {
if(index === 0 && !newData[detail]) newData[detail] = {};
})
});
console.info(newData)
I would like to take the Input and make it look like the output
You have to recursively traverse deep into the resulting object:
function parse(obj) {
const root = {};
for(const [key, value] of Object.entries(obj)) {
const parts = key.split(".");
const parent = parts.slice(0, -1).reduce((acc, part) => acc[part] || (acc[part] = {}), root);
parent[parts.pop()] = value;
}
return root;
}
You could use one forEach loop on object entries and then inside split each key on .. After that you can use reduce method on that array of keys to build nested object.
const obj = {
"action.button.submit": "Submit",
"action.button.submitting": "Submitting",
"buttons.common.add": "Add",
"buttons.common.reset": "Reset",
"constants.bom.conditional.or.mandatory.conditional": "Conditional",
"constants.bom.conditional.or.mandatory.mandatory": "Mandatory"
}
const res = {}
Object.entries(obj).forEach(([key, value]) => {
key.split('.').reduce((r, e, i, a) => {
return r[e] || (r[e] = (a[i + 1] ? {} : value))
}, res)
})
console.log(res)
Using Lodash you can do this with _.set method that takes target object, nested key and value.
const obj = {"action.button.submit": "Submit","action.button.submitting": "Submitting","buttons.common.add": "Add","buttons.common.reset": "Reset","constants.bom.conditional.or.mandatory.conditional": "Conditional","constants.bom.conditional.or.mandatory.mandatory": "Mandatory"}
const res = {}
_.forEach(_.entries(obj), ([k, v]) => _.set(res, k, v))
console.log(res)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.js"></script>

The proper ways to stack optioned promises

What would be the proper or the best way to collect all data from DB with promises, but with using native Node promises.
The goal is only to present what is selected:
const allPromises = [];
const selected = {
sectionA: true,
sectionB: false,
sectionCIds: [ 1, 2, 4 ],
};
if (selected.sectionA) {
allPromises.push(getSectionADataFromDbPromise());
}
if (selected.sectionB) {
allPromises.push(getSectionBDataFromDbPromise());
}
if (selected.sectionCIds.length > 0) {
allPromises.push(selected.sectionCIds
.map(getSectionCDataFromDbPromise)
);
}
Promise.all(allPromises)
.then((allResults) => {
if (selected.sectionA) {
dataA = allResults[0];
}
if (selected.sectionA) {
dataB = allResults[1];
}
if (selected.sectionC) {
dataC = allResults[2]; // <-- this will not work if B is not selected
}
// ... same logic to build report: return Promise.all()...
});
Possible solutions:
Track index for each data selected (eg. index of C will be 1)
Object Map
Add else { allPromises.push(Promise.resolve(null)) } to every if
Is there maybe an easier or one of this will be the proper way?
Don't use push on the arrays conditionally, but always put the same value at the same index. Even if the value is nothing - Promise.all will handle that just fine.
const selected = {
sectionA: true,
sectionB: false,
sectionCIds: [ 1, 2, 4 ],
};
Promise.all([
selected.sectionA ? getSectionADataFromDbPromise() : null,
selected.sectionB ? getSectionBDataFromDbPromise() : null,
Promise.all(selected.sectionCIds.map(getSectionCDataFromDbPromise))
]).then(([dataA, dataB, dataC]) => {
if (selected.sectionA) {
// use dataA
}
if (selected.sectionA) {
// use dataB
}
if (dataC.length) { // same as selected.selectionCIds.length
// use dataC
}
});
What do you think about this ? It's bigger, it's heavier, it's more difficult, but it's all automatized and fully evolutive. Wanna handle a new parameter ? A parameter have data now ? Change the map only.
I create a map that would contains everything we need to use a loop. The state of the data (activated or not), the function to call to get the data and so on.
const mapSelected = {
sectionA: {
state: true,
func: getSectionADataFromDbPromise,
},
sectionB: {
state: false,
func: getSectionBDataFromDbPromise,
},
sectionC: {
state: true,
func: getSectionCDataFromDbPromise,
data: [
1,
2,
4,
],
},
};
Then we create the promise array using the map we has created. Handling the case with data and without data.
const promises = Object.values(mapSelected).reduce((tmp, {
state,
func,
data,
}) => {
if (!state) return tmp;
if (data && data.length) {
return [
...tmp,
...data.map(x => func.call(this, x)),
];
}
return [
...tmp,
func.call(this),
];
});
Then we create arrays from the promise return for each key on the map. You can change how I present the data, I didn't knew what you really wanted there.
Promise.all(promises)
.then((allResults) => {
let i = 0;
const [
dataA,
dataB,
dataC,
] = Object.values(mapSelected).reduce((tmp, {
state,
data,
}, xi) => {
if (!state) return tmp;
if (data && data.length) {
data.forEach(x => (tmp[xi].push(allPromises[i++])));
return tmp;
}
tmp[xi].push(allPromises[i++]);
return tmp;
}, Object.values(mapSelected).map(() => []));
});
#EDIT
I just did a snippet about the code I've made, run it
function a() {
return 1;
}
const mapSelected = {
sectionA: {
state: true,
func: a,
},
sectionB: {
state: false,
func: a,
},
sectionC: {
state: true,
func: a,
data: [
1,
2,
4,
],
},
};
const allPromises = [
0,
1,
2,
3,
4,
];
let i = 0;
const [
dataA,
dataB,
dataC,
] = Object.values(mapSelected).reduce((tmp, {
state,
data,
}, xi) => {
if (!state) return tmp;
if (data && data.length) {
data.forEach(x => (tmp[xi].push(allPromises[i++])));
return tmp;
}
tmp[xi].push(allPromises[i++]);
return tmp;
}, Object.values(mapSelected).map(() => []));
console.log(dataA);
console.log(dataB);
console.log(dataC);
Unfortunately, unlike libraries such as Q, the standard Promise does not expose a variant of all taking an object of promises.
However, we can use the new ES2015 and ES2017 Object utility methods to assist us in keeping the code readable.
const allPromises = {};
const selected = {
sectionA: true,
sectionB: false,
sectionCIds: [1, 2, 4],
};
if (selected.sectionA) {
allPromises.sectionA = getSectionADataFromDbPromise();
}
if (selected.sectionB) {
allPromises.sectionB = getSectionBDataFromDbPromise();
}
if (selected.sectionBIds.length > 0) {
allPromises.sectionC = Promise.all(selected.sectionBIds
.map(getSectionCDataFromDbPromise)
);
}
Now we can write
Promise.all(Object.entries(allPromises).map(([key, promise]) =>
promise.then(value => ({[key]: value}))
))
.then(allResults => {
const results = Object.assign({}, ...allResults);
const data = {
a: results.sectionA,
b: results.sectionB,
c: results.sectionB && results.sectionC
};
// ... same logic to build report: return Promise.all()...
});

Javascript: variable is not undefined but while returning, being returned undefined

In the snippet below, before return(res) , I log it, and it's not undefined
but somehow, it's being returned as undefined.
What am I doing wrong?
filterData = (inputData, searchedKey) => {
inputData.forEach((data, index) => {
if(data){
if(data.hasOwnProperty(searchedKey)){
const res = data[searchedKey]
console.log(res) /// logs the results
return(res) /// returns undefined
}
var dataToProcess = [];
var fieldKeys = Object.keys(data)
fieldKeys = fieldKeys.filter((field, index) => !field.includes("#"))
fieldKeys.forEach((key, index) => {
dataToProcess.push(data[key]);
})
this.filterData(dataToProcess, searchedKey)
}
})
}
console.log(this.filterData([{"#name": "foo", "#type": "bar"}], "#type"))
Some issues:
forEach does not return anything else than undefined, so returning a value in its callback function does not do anything useful.
Your function does not return a value
The return value of the recursive call is not used.
if (data) is not good enough to make sure data is an object. For instance, it would also be true for a non-zero number. Use Object(data) === data instead.
Since there could be multiple matches (in a nested input object), your function should return an array, which is also what someone would expect when seeing your function name. Also the standard array filter method returns an array. So it would be in line with that.
Here is how you could make it work:
var filterData = (inputData, searchedKey) => {
inputData = inputData.filter( data => Object(data) === data );
return !inputData.length ? [] :
inputData.filter( data => data.hasOwnProperty(searchedKey) )
.map( data => data[searchedKey] )
// Add the results from recursion:
.concat(filterData([].concat(...
inputData.map( data =>
Object.keys(data)
.filter( key => !key.startsWith("#") )
.map( key => data[key] )
)
), searchedKey));
};
var data = [{
"#name": "foo",
"#title": "mr",
"deeper": [{
"#nope": "bad",
"deepest": [{
"nothing_here": null,
"#type": "deeper bar",
}]
}, {
"#type": "bar",
}]
}];
console.log(filterData(data, "#type"));
If you need only the first match, then use this variant:
var filterData = (inputData, searchedKey) => {
inputData = inputData.filter( data => Object(data) === data );
var res = inputData.find( data => data.hasOwnProperty(searchedKey) );
return res !== undefined ? res[searchedKey] :
// Try in nested objects:
filterData([].concat(...
inputData.map( data =>
Object.keys(data)
.filter( key => !key.startsWith("#") )
.map( key => data[key] )
)
), searchedKey);
};
var data = [{
"#name": "foo",
"#title": "mr",
"deeper": [{
"#nope": "bad",
"deepest": [{
"nothing_here": null,
"#type": "deeper bar",
}]
}, {
"#type": "bar",
}]
}];
console.log(filterData(data, "#type"));
Is this what you want to achieve?
filterData = (inputData, searchedKey) => {
return inputData.map((data, index) => {
if(data){
if(data.hasOwnProperty(searchedKey)){
const res = data[searchedKey]
console.log(res) /// logs the results
return(res) /// returns undefined
}
var dataToProcess = [];
var fieldKeys = Object.keys(data)
fieldKeys = fieldKeys.filter((field, index) => !field.includes("#"))
fieldKeys.forEach((key, index) => {
dataToProcess.push(data[key]);
})
this.filterData(dataToProcess, searchedKey)
}
})
}
console.log(this.filterData([{"#name": "foo", "#type": "bar"}], "#type"))
Use Array#map(), it's pretty useful in many cases.

Categories

Resources