cannot add property 1 , onject is not extensible in react - javascript

anyone has an idea what causes the ff issue ? I cannot insert new object to a key object in my arrays of objects for example I wanna insert new email to emails at index 1 when I push to that it causes error cannot add property 1 , onject is not extensible in .
Ideads and help would be much appreciated. Thank.s
#code
const addEmail = (id: number) => {
console.log('...RegionalList',RegionalList)
const regionalList = [...RegionalList];
const index = regionalList
.map((prop: IRegionalList) => prop.id)
.indexOf(id);
console.log('regionalList[index].emails' , regionalList[index].emails)
regionalList[index].emails.push({
emailAddress: '',
firstName: '',
lastName: '',
id: Math.floor(Math.random() * 999),
fetching: false,
});
setRegionalList(regionalList);
};
#object where I am inserting on regionalist arrays of object the value of this variable const regionalList = [...RegionalList];
[
{
"id": 4,
"name": "Associate Director of Construction Ops",
"column": "associateDirectorofConstructionOps",
"emails": [
{
"id": 79,
"emailAddress": "crawform#raw.com",
"firstName": "James",
"lastName": "Crawford"
}
]
},
{
"id": 5,
"name": "CAM Manager",
"column": "camManager",
"emails": [
{
"id": 77,
"emailAddress": "jenn.jones4#test.com",
"firstName": "Jennifer",
"lastName": "Jones"
}
]
},
]
#another snippet
const setEmailValue = (event: any, regionalId: number, index: number) => {
setRegionalList((prevState: IRegionalList[]) => {
const newState = prevState.map((prop: IRegionalList) => {
if (prop.id === regionalId) {
prop.emails[index] = { emailAddress: event.target.value, id: null };
return { ...prop };
}
return prop;
});
return newState;
});
}
#another snippet
useEffect(() => {
if (selectedRow) {
console.log('selectedRow' , selectedRow)
// Set selected row data
setData({
regionName: selectedRow['regionName'],
marketName: selectedRow['marketName'],
subRegionName: selectedRow['subRegionName'],
});
let regional = [...RegionalList];
for (const k in selectedRow) {
regional.map((prop: IRegionalList) => {
if (prop.column === k) {
prop.emails = selectedRow[k] ? selectedRow[k] : []
}
})
}
console.log('regional:', regional);
setRegionalList(regional);
}
}, [selectedRow]);

As you cannot mutate the state as in your code above, you have to create and return a new array, so try:
const addEmail = (id: number) => {
setRegionalList(list => list.map(item => {
if (item.id === id) {
return {
...item,
emails: [
...item.emails,
{
emailAddress: '',
firstName: '',
lastName: '',
id: Math.floor(Math.random() * 999),
fetching: false,
}
]
}
}
return item;
}))
};

Related

How to store multiple values in to objects within an array

Problem:
I have an api and each object within the api doesn't have a value. I would like to add a unique value to each object within the array so that I can create a function and use 'e.target.value' with event listeners. I'm doing this in nextjs.
Why:
Because I want to store each value in to an array and localstorage before eventually displaying the data that was stored in the array as like a favorites item.
Is there a way of doing this ?
Information:
data.items has over 30+ objects -
"items": [
{
"id": 119603782,
"node_id": "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
"name": "react-contextual",
"full_name": "drcmda/react-contextual",
"private": false,
"owner": {
"login": "drcmda",
"id": 2223602,
}
{
"id": 119603782,
"node_id": "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
"name": "react-contextual",
"full_name": "drcmda/react-contextual",
"private": false,
"owner": {
"login": "drcmda",
"id": 2223602,
}
So far my data has been sorted and mapped like so.
{data.items
.sort(function (a, b) {
return b.stargazers_count - a.stargazers_count && new Date (b.created_at) - new Date(a.created_at)
})
.map((d) => (
<div onClick={checkId} key={d.id} className=" border-white p-5">
<h1 className="text-2xl font-bold">Repo name: {d.name}</h1>
An example using index as unique for displaying purpose.
const data = {
items: [
{
id: 119603782,
node_id: "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
name: "react-contextual",
full_name: "drcmda/react-contextual",
private: false,
owner: {
login: "drcmda",
id: 2223602,
},
},
{
id: 119603782,
node_id: "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
name: "react-contextual",
full_name: "drcmda/react-contextual",
private: false,
owner: {
login: "drcmda",
id: 2223602,
},
},
],
};
const items = data.items.map((item, idx) => ({ ...item, idx }));
// new item
const newItem = {
id: 119603782,
node_id: "MDEwOlJlcG9zaXRvcnkxMTk2MDM3ODI=",
name: "react-contextual",
full_name: "drcmda/react-contextual",
private: false,
owner: {
login: "drcmda",
id: 2223602,
},
};
function addNewItems(items, newItem) {
newItem.idx = items.length;
items.push(newItem);
return items;
}
console.log(addNewItems(items, newItem));
If you want add a VALUE key to each object you can do the following:
const [isLoading, setIsLoading] = useState(false),
[items, setItems] = useState([]);
useEffect(() => {
(async () => {
setIsLoading(true);
try {
//you can replace axios with fetch
const res = await axios('https://your-api'),
// clone deep is from lodash => you can use the spread (...) operator if you want
clonedItems = cloneDeep(res.data.items);
clonedItems.forEach((el) => {
// add whatever you want in the (value)
el.value = 'required value';
});
//sort items based on the required key
clonedItems.sort((a, b) => {
//replace name with your key
if (a.name.toLowerCase() < b.name.toLowerCase()) {
return -1;
}
if (a.name.toLowerCase() > b.name.toLowerCase()) {
return 1;
}
return 0;
});
//check the modified items
console.log(clonedItems);
setItems(clonedItems);
} catch (err) {
//replace it with your error handler code
console.log(err);
} finally {
setIsLoading(false);
}
})();
}, []);
Notes:
You should sort your elements before storing it in the state
You can replace axios with fetch
you can use the spread operator (...) in place of cloneDeep from lodash

Javascript Recursion Issue - deeply nested object

I have a function which loops over the keys of an object and looks for sensitive keys like email, key, password, apiKey, secrets, secret and userKey.
If it finds any that have a value, it redacts the value.
However, its failing sometimes with an error like:
"RangeError: Maximum call stack size exceeded"
Whats causing the endless recursion??
const deepObjectRedactor = obj => {
const sensitiveKeys = [
'email',
'key',
'password',
'apiKey',
'secrets',
'secret',
'userKey'
];
Object.keys(obj).forEach(key => {
if (
sensitiveKeys.map(e => e.toLowerCase()).includes(key.toLowerCase()) &&
obj[key]
) {
obj[key] = '**********';
return;
}
if (obj[key] && typeof obj[key] === 'object') {
deepObjectRedactor(obj[key]);
}
});
};
// function invoking the redactor
const customRedactorFormat = info => {
if (info.message && typeof info.message === 'object') {
deepObjectRedactor(info.message);
}
return info;
});
You can write a generic map that works for objects and arrays. And then write redact as a specialization of map -
function map (t, f)
{ switch (t?.constructor)
{ case Array:
return t.map((v, k) => f(k, map(v, f)))
case Object:
return Object.fromEntries(
Object.entries(t).map(([k, v]) => [k, f(k, map(v, f))])
)
default:
return t
}
}
const redact = (t, keys = new Set) =>
map(t, (k, v) => keys.has(k) ? "*****" : v)
const data =
[ { user: 1, cc: 1234, password: "foo" }
, { nested: [ { a: 1, pin: 999 }, { b: 2, pin: 333 } ] }
, { deeply: [ { nested: [ { user: 2, password: "here" } ] } ] }
]
console.log(redact(data, new Set(["cc", "password", "pin"])))
[
{
"user": 1,
"cc": "*****",
"password": "*****"
},
{
"nested": [
{
"a": 1,
"pin": "*****"
},
{
"b": 2,
"pin": "*****"
}
]
},
{
"deeply": [
{
"nested": [
{
"user": 2,
"password": "*****"
}
]
}
]
}
]

Convert Object which has value an Array to another Array of Object

I have this kind of object:
{
"John":[
{
"address":"xxx1",
"city":"yyy1"
},
{
"address":"xxx2",
"city":"yyy2"
}
],
"Doe":[
{
"address":"aaaa1",
"city":"aaa1"
}
],
"Smith":[
{
"address":"bbb1",
"city":"bbb1"
}
],
}
What I try to achieve is to reduce this object so it look like this:
[
{
"name":"John",
"address":"xxx1",
"city":"yyy1"
},
{
"name":"John",
"address":"xxx2",
"city":"yyy2"
},
{
"name":"Doe",
"address":"aaaa1",
"city":"aaaa1"
},
{
"name":"Smith",
"address":"bbb1",
"city":"bbb1"
}
]
But I'm sure that the same thing can be done somehow by using the ES6 array.reduce. Can you help me? I looked at JS (ES6): Reduce array based on object attribute but I can't figure it out.
const modifiedData = Object.entries(data).reduce(function (acc, [key,value]) {
const personName = key;
return [
...acc,
{
Agent: personName ,
adress: value.adress
},
];
}, []);
You can achieve this using reduce.
const obj = {
John: [
{
address: "xxx1",
city: "yyy1",
},
{
address: "xxx2",
city: "yyy2",
},
],
Doe: [
{
address: "aaaa1",
city: "aaa1",
},
],
Smith: [
{
address: "bbb1",
city: "bbb1",
},
],
};
const result = Object.entries(obj).reduce((acc, [key, arr]) => {
const collection = arr.map((a) => ({ name: key, ...a }));
acc = [...acc, ...collection];
return acc;
}, []);
console.log( result );
The simple way like this.
const data = {"John":[{"address":"xxx1","city":"yyy1"},{"address":"xxx2","city":"yyy2"}],"Doe":[{"address":"aaaa1","city":"aaa1"}],"Smith":[{"address":"bbb1","city":"bbb1"}],};;
const result = Object.entries(data).flatMap(([key, values]) =>
values.map(o => ({name: key, ...o})));
console.log(result);
If you want to do it using Array.prototype.reduce, you can do something like this:
const input = {
"John": [{
"address": "xxx1",
"city": "yyy1"
},
{
"address": "xxx2",
"city": "yyy2"
}
],
"Doe": [{
"address": "aaaa1",
"city": "aaa1"
}
],
"Smith": [{
"address": "bbb1",
"city": "bbb1"
}],
}
// 1. Using Object.keys()
const output1 = Object.keys(input).reduce((acc, person) => {
input[person].forEach(item => {
acc.push({ name: person, ...item })
})
return acc;
}, []);
console.log('output1:', output1)
// 2. Using Object.entries()
const output2 = Object.entries(input).reduce((acc, [key, value]) => {
value.forEach(item => {
acc.push({ name: key, ...item })
});
return acc;
}, [])
console.log('output2:', output2);

How to filter in two deep arrays

I'm looking to filter in two deep arrays, actually my JSON:
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
},
"1": {
"product":[{
"uuid":"uid",
"name":"Milk"
}]
}
}
I would like to get something like that when I filter with the word "ric":
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
}]
}
}
But I got this result:
{
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
}
}
My code:
dataSort.categories = the json and
event.target.value.toLowerCase() = the specific word
dataSort.categories.filter(s => s.products.find(p => p.name.toLowerCase().includes(event.target.value.toLowerCase())));
You can achieve this with a combination of reduce and filter
var input = {
"0": {
"product":[{
"uuid":"uid",
"name":"Rice"
},
{
"uuid":"uid",
"name":"Pasta"
}]
},
"1": {
"product":[{
"uuid":"uid",
"name":"Milk"
}]
}
}
var search = "ric"
var result = Object.entries(input).reduce( (acc, [key,val]) => {
found = val.product.filter(x => x.name.toLowerCase().includes(search.toLowerCase()))
if(found.length){
acc[key] = {...val, product: found}
}
return acc
},{})
console.log(result)
There is many approach to do this, one is to map your top level array to the subArrays filtered results then filter it after:
dataSort.categories
.map(s => s.products.filter(p => p.name.toLowerCase().includes(event.target.value.toLowerCase())))
.filter(s => !!s.products.length);
You may also prefer to get a "flat" array as result because it is easier to use after :
dataSort.categories
.reduce((acc, s) => [...acc, s.products.filter(p => p.name.toLowerCase().includes(event.target.value.toLowerCase()))], []);
Please find below the code to filter out values inside the product.name and only return the value which are matching the equality condition in product array.
const json = [
{
product: [
{
uuid: "uid",
name: "Rice",
},
{
uuid: "uid",
name: "Pasta",
},
],
},
{
product: [
{
uuid: "uid",
name: "Milk",
},
],
},
];
const inputValue = "rIc";
const filteredArray = [];
json.map((s) => {
const item = s.product.find((p) =>
p.name.toLowerCase().includes(inputValue.toLowerCase())
);
item && filteredArray.push({ product: item });
});
console.dir(filteredArray);
Your dataset is an Object, not an Array and the filter is an Array method. You can use reduce by looping on the object values by Object.values then filter your products array.
const data = {
'0': {
product: [
{
uuid: 'uid',
name: 'Rice',
},
{
uuid: 'uid',
name: 'Pasta',
},
],
},
'1': {
product: [
{
uuid: 'uid',
name: 'Milk',
},
],
},
};
const keyword = 'ric';
const dataset = Object.values(data);
const results = dataset.reduce((acc, item, index) => {
const search = keyword.toLowerCase();
const product = item.product.filter(product => product.name.toLowerCase().includes(search));
if (product.length) acc[index] = { ...item, product };
return acc;
}, {});
console.log(results);

Relate and merge array of same Department

I am working on an application where I need to get combine the object of same department based on the
conditions provided in the second Array and attach the relation to the object.
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"}
]
Output
outArr ={
[{"ID":"1","NAME":"KEN","DEPT1":"CSE","REL":"AND"},
{"ID":"2","NAME":"MARK","DEPT2":"IT","REL":"AND"}], //Arr1
[{"ID":"3","NAME":"TOM","DEPT3":"ECE","REL":"OR"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB","REL":"OR"}], //Arr2
[{"ID":"5","NAME":"TIM","DEPT5":"SEC"}] //Arr3
}
Code:
let condArr=[],outArr,i=1;
inArr1.forEach(condt => {
let dept = Object.keys(condt)[0];
let tmparr = dept.split("D");
tmparr.shift()
condArr.push(tmparr)
});
inArr2.forEach(condt => {
if(condArr.includes(inArr2.D+i)){
i++;
outArr.push(inArr2);
}
});
Your code has a bit confused logic, i would suggest rather this
let inArr1 = [{"D1D2":"AND"},{"D3D4":"OR"},{"D5D6":"AND"}]
let inArr2 =[{"ID":"1","NAME":"KEN","DEPT1":"CSE"},
{"ID":"2","NAME":"MARK","DEPT2":"IT"},
{"ID":"3","NAME":"TOM","DEPT3":"ECE"},
{"ID":"4","NAME":"SHIV","DEPT4":"LIB"},
{"ID":"5","NAME":"TIM","DEPT5":"SEC"},
{"ID":"6","NAME":"TLA","DEPT6":"SEC"},
]
// first lets create object of ids as keys and conditions as values
const [keys, conditions] = inArr1.reduce((agg, cond, index) => {
Object.entries(cond).forEach(([key, value]) => {
key.split('D').forEach(v => { if (v) agg[0][v] = { value, index }})
agg[1].push([])
})
return agg
}, [{}, []]) // {1: "AND", 2: "AND", 3: "OR", 4: "OR"}
conditions.push([])
// and now just map over all elements and add condition if we found id from the keys
inArr2.forEach(item => {
const cond = keys[item.ID]
if (cond) conditions[cond.index].push({...item, REL: cond.value})
else conditions[conditions.length - 1].push(item)
})
const res = conditions.filter(v => v.length)
console.log(res)
You could store the goups by using the ID and use new objects.
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }],
inArr2 = [{ ID: "1", NAME: "KEN", DEPT1: "CSE" }, { ID: "2", NAME: "MARK", DEPT2: "IT" }, { ID: "3", NAME: "TOM", DEPT3: "ECE" }, { ID: "4", NAME: "SHIV", DEPT4: "LIB" }, { ID: "5", NAME: "TIM", DEPT5: "SEC" }],
groups = inArr1.reduce((r, o) => {
Object.entries(o).forEach(([k, REL]) => {
var object = { REL, group: [] };
k.match(/[^D]+/g).forEach(id => r[id] = object);
});
return r;
}, {}),
grouped = inArr2.reduce((r, o) => {
var { REL, group } = groups[o.ID] || {};
if (group) {
if (!group.length) r.push(group);
group.push(Object.assign({}, o, { REL }));
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
can try other solution:
let inArr1 = [{ D1D2: "AND" }, { D3D4: "OR" }, { D6D7: "XOR" }];
let inArr2 = [
{ ID: "1", NAME: "KEN", DEPT1: "CSE" },
{ ID: "2", NAME: "MARK", DEPT2: "IT" },
{ ID: "3", NAME: "TOM", DEPT3: "ECE" },
{ ID: "4", NAME: "SHIV", DEPT4: "LIB" },
{ ID: "5", NAME: "TIM", DEPT5: "SEC" },
{ ID: "9", NAME: "BAR", DEPT5: "XYZ" },
{ ID: "6", NAME: "FOO", DEPT5: "XYZ" },
];
let unmatchedArr = []
let matchedArr = inArr2.reduce((acc, obj) => {
// getting index matched from inArr1 objects key
const indexMatched = getIndexMatch(obj.ID);
// creating index if not exists
if (!acc[indexMatched] && indexMatched !== null) acc[indexMatched] = [];
// if some index matched it merge current obj with DEL property with inArr1[indexMatched] key => value
return indexMatched !== null
? acc[indexMatched].push({
...obj,
DEL: inArr1[indexMatched][Object.keys(inArr1[indexMatched])[0]]
})
// pushing on unmatchedArr
: unmatchedArr.push(obj)
, acc
}, []);
function getIndexMatch(id) {
for (const [index, obj] of inArr1.entries()) {
for (const key of Object.keys(obj)) {
// spliting only digits of the current key of object
if (key.match(/\d/g).includes(id)) return index; // returning index of inArr1 if is included
}
}
return null;
}
// merging arrays
const result = [...matchedArr, unmatchedArr];
console.log(result);

Categories

Resources