Want to create a common method inside map function in react - javascript

Below is my code
const newState = permissionData.map(obj => {
if (obj.component.includes('Client')) {
if ((permissionName.includes('Client')) && (name.includes('create'))) {
return { ...obj, createPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Client')) && (name.includes('view'))) {
return { ...obj, viewPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Client')) && (name.includes('delete'))) {
return { ...obj, deletePermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Client')) && (name.includes('update'))) {
return { ...obj, updatePermission: event.target.checked ? 1 : 0 };
}
}
if (obj.component.includes('Project')) {
if ((permissionName.includes('Project')) && (name.includes('create'))) {
return { ...obj, createPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Project')) && (name.includes('view'))) {
return { ...obj, viewPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Project')) && (name.includes('delete'))) {
return { ...obj, deletePermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Project')) && (name.includes('update'))) {
return { ...obj, updatePermission: event.target.checked ? 1 : 0 };
}
}
if (obj.component.includes('Schema')) {
if ((permissionName.includes('Schema')) && (name.includes('create'))) {
return { ...obj, createPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Schema')) && (name.includes('view'))) {
return { ...obj, viewPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Schema')) && (name.includes('delete'))) {
return { ...obj, deletePermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Schema')) && (name.includes('update'))) {
return { ...obj, updatePermission: event.target.checked ? 1 : 0 };
}
}
if (obj.component.includes('Dataset')) {
if ((permissionName.includes('Dataset')) && (name.includes('create'))) {
return { ...obj, createPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Dataset')) && (name.includes('view'))) {
return { ...obj, viewPermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Dataset')) && (name.includes('delete'))) {
return { ...obj, deletePermission: event.target.checked ? 1 : 0 };
}
if ((permissionName.includes('Dataset')) && (name.includes('update'))) {
return { ...obj, updatePermission: event.target.checked ? 1 : 0 };
}
}
return obj;
});
Above code is working well, however since the code is repetitive I want to make a common method to optimize the code.
But not sure how can I do it.
I am trying something like this -
const checkPermission = (obj: object, type: string, permissionName: string, name: string, checked: boolean) => {
if (obj.component.includes(type)) {
if ((permissionName.includes(type)) && (name.includes('create'))) {
return { ...obj, createPermission: checked };
}
if ((permissionName.includes(type)) && (name.includes('view'))) {
return { ...obj, viewPermission: checked };
}
if ((permissionName.includes(type)) && (name.includes('delete'))) {
return { ...obj, deletePermission: checked };
}
if ((permissionName.includes(type)) && (name.includes('update'))) {
return { ...obj, updatePermission: checked };
}
}
};
And I am calling this method like this -
checkPermission(obj, 'Client', permissionName, name, event.target.checked);
But it's not working. Any pointers/ideas as how to do it.

The ['Client', 'Project', 'Schema', 'Dataset'] strings are all used identically, so put them into an array and .find the one that matches the .includes conditions, removing one dimension of repetitiveness.
After that, the proper action can be found by similarly iterating over an array of ['create', 'view', 'delete', 'update']. With that, you can update the property on the state object with bracket notation.
const newState = permissionData.map(obj => {
const entity = ['Client', 'Project', 'Schema', 'Dataset'].find(str => obj.component.includes(str));
if (!entity || !permissionName.includes(entity)) {
return obj;
}
const action = ['create', 'view', 'delete', 'update'].find(a => name.includes(a));
return action
? { ...obj, [action + 'Permission']: event.target.checked ? 1 : 0 }
: obj;
});
If possible, you might consider a better state structure - instead of having, for example
{
createPermission: 1
// other possible Permission keys
}
If the object doesn't contain any keys other than Permission keys, the Permission suffix doesn't add any useful information - rename the state array variable name to include Permission, and then you no longer have to use the ugly lookup with bracket notation.
If the object can contain non-Permission keys, then consider changing it so that Permission is a sub-property. And if the Permission values can only be 0 or 1, it might be more appropriate to use booleans.
{
// state properties...
// and also
permissions: {
create: true
// etc
}
}
Or have an array of enabled permissions.
permissions: ['create']
Or, if only one is possible, a single string.
permissions: 'create'

Related

Compare nested objects and list out differences in JavaScript

I want the difference in such a way that the I don't return the entire nested object if any of the values is different.
I have seen solutions online and they all return the entire nested objects and it doesn't work if only 1 key-value pair is changed. i don't want to show the difference as a complete nested object. it should be easier for any user to read.
for eg:
const A = {
position: 2,
attributes: [{
code: 123,
name: "xyz",
params: {
label: "hehe",
units: "currency"
}
}],
code: 1
}
const B = {
position: 3,
attributes: [{
code: 123,
name: "xyzr",
params: {
label: "heh",
units: "currency"
}
}],
code: 1
}
I want the output to be like this:
difference: {
position: {
current: 2,
previous: 3
},
attributes: {
current : [{ name: "xyz", params: { label: "hehe" } }],
previous: [{ name: "xyzr", params: {label: "heh"}}]
}
}
The code that I tried:
const compareEditedChanges = (A: any, B: any) => {
const allKeys = _.union(_.keys(A), _.keys(B));
try {
setDifference(
_.reduce(
allKeys,
(result: any, key) => {
if (!_.isEqual(A?.[key], B?.[key])) {
result[key] = {
current: A[key],
previous: B[key]
};
}
return result;
},
{}
)
);
} catch (err) {
console.error(err);
}
return difference;
};
After giving it a lot of thought to the code, I came with my own solution for a deeply nested objects comparison and listing out the differences in an object with keys as current and previous.
I didn't use any inbuilt libraries and wrote the code with simple for loop, recursion and map
const compareEditedChanges = (
previousState,
currentState
) => {
const result = [];
for (const key in currentState) {
// if value is string or number or boolean
if (
typeof currentState[key] === 'string' ||
typeof currentState[key] === 'number' ||
typeof currentState[key] === 'boolean'
) {
if (String(currentState[key]) !== String(previousState[key])) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// if an array
if (
Array.isArray(currentState[key]) ||
Array.isArray(previousState[key])
) {
console.log(currentState[key])
if (currentState[key].length > 0 || previousState[key].length > 0) {
currentState[key].map((value, index) => {
// check for array of string or number or boolean
if (
typeof value === 'string' ||
typeof value === 'number' ||
typeof value === 'boolean'
) {
if (
JSON.stringify(currentState[key]) !==
JSON.stringify(previousState[key])
) {
result.push({
[key]: {
current: currentState[key],
previous: previousState[key]
}
});
}
}
// check for array of objects
if (typeof value === 'object') {
const ans = compare(
value,
previousState[key][index]
);
result.push(ans);
}
});
}
}
}
return result;
};
You first need a object:
const [object, setObject] = useState({
number: 0,
text: "foo"
});
You need to check when the object changed with useEffect, but you also need to see the previos object, for that we will be using a helper function.
const prevObject = usePrevious(object);
const [result, setResult] = useState("");
useEffect(() => {
if (prevObject) {
if (object.number != prevObject.number) {
setResult("number changed");
}
if (object.text != prevObject.text) {
setResult("text changed");
}
}
}, [object]);
//Helper function to get previos
function usePrevious(value) {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
}
Here is the Codesandbox

Recursively convert an object fields from camelCase to UPPERCASE

I have tried to recursively convert an object fields from camelCase to UPPERCASE.
For some reason it wont work, I have tried many different ways from here in stackoverflow.
Would apprecaite for all the help, thanks.
const o = {
KeyFirst: "firstVal",
KeySecond: [
{
KeyThird: "thirdVal",
},
],
KeyFourth: {
KeyFifth: [
{
KeySixth: "sixthVal",
},
],
},
};
function renameKeys(obj) {
return Object.keys(obj).reduce((acc, key) => {
const value = obj[key];
const modifiedKey = `${key[0].toLowerCase()}${key.slice(1)}`;
if (Array.isArray(value)) {
return {
...acc,
...{ [modifiedKey]: value.map(renameKeys) },
};
} else if (typeof value === "object") {
return renameKeys(value);
} else {
return {
...acc,
...{ [modifiedKey]: value },
};
}
}, {});
}
console.log(renameKeys(o));
You can recursively loop over the object and transform the keys to be uppercase.
const o = {
KeyFirst: { KeySecond: "secondVal" },
KeyThird: [{ KeyFourth: "fourthVal" }],
KeyFifth: {
KeySixth: [{ KeySeventh: "seventhVal" }],
},
};
function renameKeys(obj) {
if (Array.isArray(obj)) {
return obj.map((o) => renameKeys(o));
} else if (typeof obj === "object" && obj !== null) {
return Object.entries(obj).reduce(
(r, [k, v]) => ({ ...r, [k.toUpperCase()]: renameKeys(v) }),
{}
);
} else {
return obj;
}
}
console.log(renameKeys(o));

get list from axios with filter options- gets undifined

i know its a popular question but i still couldent get an asnwer.
i have an axiox get, like this
function query(filterBy) {
return axios.get(TOY_URL,{ params: { filterBy } })
.then(res=>res.data)
.catch(err => {
console.log('Couldn\'t load toys please try again',err);
})
}
on server side :
app.get("/api/toy", (req, res) => {
let { filterBy } = req.query;
if(filterBy !== undefined) filterBy=JSON.parse(filterBy)
else{console.log('why !!');}
toysService.query(filterBy).then((toys) => res.send(toys));
and next step is :
function query(filterBy){
console.log(filterBy);
const regex = new RegExp(filterBy.name, 'i')
return Promise.resolve(
gToys
.filter(toy => {
return regex.test(toy.name)
})
.filter(toy => {
const s = filterBy.inStock
return s === 'TRUE' ? toy.inStock : s === 'FALSE' ? !toy.inStock : toy
})
.filter(toy => {
return toy.type.includes(filterBy.type)
})
.sort((a, b) => {
if (filterBy.sortBy === 'NAME') {
return a.name > b.name ? 1 : a.name < b.name ? -1 : 0
} else if (filterBy.sortBy === 'PRICE') {
return a.price > b.price ? 1 : a.price < b.price ? -1 : 0
}
})
)
}
i get an error : Cannot read property 'name' of undefined
the weird part is that on all console.log on cmd , i get this :
{}
undefined
why !!
undefined
TypeError: Cannot read property 'name' of undefined ...
{ filterBy: '{"name":"","price":0,"type":"all","inStock":"all"}' }
{"name":"","price":0,"type":"all","inStock":"all"}
{ name: '', price: 0, type: 'all', inStock: 'all' }
i want to get a list (comes grom gList) even if i dont do any kind of filtering.
thank you .
I think the query parameter maybe URL encoded

Double for loop without mutating prop, VUE3

I have a 'data' props which say looks like this:
data = [
{
"label":"gender",
"options":[
{"text":"m","value":0},
{"text":"f","value":1},
{"text":"x", "value":null}
]
},
{
"label":"age",
"options":[
{"text":"<30", "value":0},
{"text":"<50","value":1},
{"text":">50","value":3}
]
}
]
In a computed property I want to have a new array which looks exactly like the data prop, with the difference that - for the sake of example let's say - I want to multiply the value in the options array by 2. In plain js I did this before, like this:
data.forEach(item => {
item.options.forEach(option => {
if (option.value !== null && option.value !== 0) {
option.value *= 2;
}
})
});
Now I'm trying to do this in a computed property, with .map(), so it doesn't mutate my data props, but I cant figure out how.
computed: {
doubledValues() {
var array = this.data.map((item) => {
//...
item.options.map((option) => {
//... option.value * 2;
});
});
return array;
}
}
you can use map() method, like so:
computed: {
doubledValues() {
return this.data.map(item => ({...item, options: item.options.map(obj => {
return (obj.value != null) ? { ...obj, value: obj.value * 2 } : { ...obj }
})})
);
}
}
Just copy objects/arrays. It will be something like that
computed: {
doubledValues() {
return this.data.map((item) => {
const resultItem = {...item};
resultItem.options = item.options.map((option) => {
const copyOption = {...option};
if (copyOption.value !== null && copyOption.value !== 0) {
copyOption.value *= 2;
}
return copyOption;
});
return resultItem;
});
}
}

How to check if a dynamic collection of objects have the same keys recursively?

I am given an object which has dynamic properties, but all of its properties values are objects too (keys at this level can differ which is fine). But for each of these objects, I want to make sure all of them have the same keys and recursively too. For example
Given A where
A = {
'en' : {
's1' : '',
's2' : {
's3' : '',
's4' : ''
}
},
'fr' : {
's1' : '',
's2' : {
's3' : '',
's4' : ''
}
}
}
these two are the same, because they have the same key values recursively.
A = {
'en' : {
's1' : '',
's2' : {
's3' : '',
's4' : ''
}
},
'fr' : {
's1' : '',
's2' : {
's3' : '',
's5' : ''
}
}
}
this would be false because s5 is different and don't exist in at least one other.
Essentially the purpose is to create an object which has language objects inside and I can put any language objects I want, so its dynamic. But for each language object, all it's keys should be synchronized and the same with all other language object.
I have this
this.compareKeys = function(dataObj) {
var checkObj = {};
for(var prop in dataObj) {
if (dataObj.hasOwnProperty(prop)) {
var obj = dataObj[prop];
var keysStr = JSON.stringify(Object.keys(obj).sort());
var keys = Object.keys(checkObj);
if (keys.length == 0) {
checkObj[keysStr] = true;
} else if (!checkObj.hasOwnProperty(keysStr)) {
return false;
}
}
}
return true;
}
but how do I make it work for recursively? This only checks 1 level deep.
Thanks
You can compare the lang object with a structure object:
var struct = {
's1' : '',
's2' : {
's3' : '',
's4' : ''
}
}
var lang = {
'en' : {
's1' : '',
's2' : {
's3' : '',
's4' : ''
}
},
'fr' : {
's1' : '',
's2' : {
's3' : '',
's4' : ''
}
}
}
Therefore get all keys in the lang object recursively:
function getAllKeys(obj, res) { // Get all keys in string format
for(k in obj) {
res.push(k);
if(typeof(obj[k]) === "object") {
var o = obj[k];
getAllKeys(o, res);
}
}
return res.join("");
}
You give this function an object and a result array as arguements:
function f(obj, []) { ... }
Check if the value is an object:
if(typeof(obj[k]) === "object") { ... }
If so run the function again with obj[k] as the new object and so on.
For comparison reasons the function outputs a string!
The next step is to compare lang object with structure object:
function compareObjWithStructure(obj, struct) {
var s = getAllKeys(struct, []);
for(k in obj) {
var ks = getAllKeys(obj[k], []);
if(ks !== s) {
return false;
} else {
return true;
}
}
}
JSFiddle
You can compare two objects level by level with passing keys array.
if (obj1.constructor === Array && obj2.constructor === Array) {
flag = obj1.length === obj2.length;
if (flag) {
obj1.forEach(function(key) {
map[key] = (map[key] || 0) + 1;
});
obj2.forEach(function(key) {
flag = flag && (map[key] !== undefined);
});
}
return flag;
}
If this level keys are the same, then check their children.
Object.keys(obj1).forEach(function(key) {
if (typeof obj1[key] === 'object') {
flag = flag && compareKeys(obj1[key], obj2[key]);
}
});
Here is the jsfiddle link
Hope this is useful.

Categories