Appending to an array access by a key in Redux Reducer - javascript

I'm trying to add to my state within a reducer. I need to access a key, and then add to an array within that key. This is what my state currently looks like:
{teamMembers: {
PersonId1: [{ object1 }, {object2 }]
PersonId2: [{ object3 }, { object4 }]
PersonId3: [{ object5 }, { object6 }]
}}
I need to access a PersonId, based on what the action inputs, and then append an item from the action to the array. Ideally it would also create a new key and array if the key PersonId didn't already exist.

In your reducer, just do a little data manipulation. Remember to only manipulate a copy of your state and not your actual state.
const action = {
payload: {
personId: 'PersonId4',
item: {}
}
}
const state = {
PersonId1: [{ object1 }, {object2 }]
PersonId2: [{ object3 }, { object4 }]
PersonId3: [{ object5 }, { object6 }]
}
const {personId, item} = action.payload
let newState = {...state}
if (personId in newState) {
newState[personId].push(item)
} else {
newState[personId] = [item]
}
return newState

You can conditionally check for the existence of the key and then add it to an already existing array or create a new one.
Something like this:
const newItems = state[id] ? [...state[id], ...items] : items;
//...
return {
...state,
[id]: newItems
}
Note that i'm using the object spread syntax which is a proposal (in stage 3) and you need the babel plugin babel-plugin-transform-object-rest-spread to support it (or babel stage 3 preset).
Running example:
const initialState = {
teamMembers: {
PersonId1: [{
name: 'object1'
}, {
name: 'object2'
}],
PersonId2: [{
name: 'object3'
}, {
name: 'object4'
}],
PersonId3: [{
name: 'object5'
}, {
name: 'object6'
}]
}
};
const actionOne = {
type: 'ADD',
payload: {
id: 'PersonId2',
items: [{
name: 'object444'
}]
}
}
const actionTwo = {
type: 'ADD',
payload: {
id: 'PersonId777',
items: [{
name: 'object777'
}]
}
}
const reducer = (state = {}, action) => {
switch (action.type) {
case 'ADD':
{
const {
id,
items
} = action.payload;
const newItems = state[id] ? [...state[id], ...items] : items;
return {
...state,
[id]: newItems
}
}
default:
return state;
}
}
const reducerResultOne = reducer(initialState.teamMembers, actionOne);
console.log('reducerResultOne', reducerResultOne);
const reducerResultTwo = reducer(initialState.teamMembers, actionTwo);
console.log('reducerResultTwo', reducerResultTwo);

Related

Update an array based on the value of object

Iam having an array of object named finalArr and and object named replaceJsonData. If replaceJsonData contains add =1 , then value of REQUEST_TYPE in finalArr should also become 1
finalArr = [{
REQUEST_TYPE: 'add',
TIMESTAMP: '1671636661867',
}, ]
let replaceJsonData = {
"REQUEST_TYPE": {
'add': 1,
'modify': 2
}
}
I tried like this way , but value itself is in the form a key
finalArr.map((ele)=>{
Object.entries(replaceJsonData ).forEach(
([replaceDataKey, replaceDataValue]) => {
if (ele[replaceDataKey]) {
ele[replaceDataKey]=replaceDataValue
}
}
)
});
Expected Output:
finalArr = [{
REQUEST_TYPE: 1,
TIMESTAMP: '1671636661867',
}, ]
Use the property of replaceDataValue that matches the old value of the property being updated: replaceDataValue[ele[replaceDataKey]]
let finalArr = [{
REQUEST_TYPE: 'add',
TIMESTAMP: '1671636661867',
}, ]
let replaceJsonData = {
"REQUEST_TYPE": {
'add': 1,
'modify': 2
}
};
finalArr.map((ele) => {
Object.entries(replaceJsonData).forEach(
([replaceDataKey, replaceDataValue]) => {
if (ele[replaceDataKey]) {
ele[replaceDataKey] = replaceDataValue[ele[replaceDataKey]];
}
}
)
});
console.log(finalArr);

Clean way to reassign an object in es6 with conditionality

I'm new to ES6 and I can't find a cleaner way to make this code.
Can I get the child object if it exists or create it otherwise? To remove these ifs.
if (object?.tabs?.data) {
object = {
...object,
tabs: {
...object?.tabs,
data: {
...object?.tabs?.data,
id: newId,
},
},
};
} else if (object?.tabs) {
object = {
...object,
tabs: {
...object?.tabs,
data: {
id: newId,
},
},
};
} else {
object = {
...object,
tabs: {
data: {
id: newId,
},
},
};
}
this can be a cleaner way:
let newId = 1;
let object = {
tabs: {
data1: {
id: 2
}
}
};
let o = (object = object ?? {});
for (let [k, v] of Object.entries({
tabs: object?.tabs,
data: object?.tabs?.data ?? {id: newId}
}))
o = (o[k] = v ?? {});
console.log(object)

transform an array of objects into an array containing arrays of objects

I have an array of objects like this:
[{...}, {...}, {...}, {...}, {...}]
An object looks like this:
{
id: ...
name: ...
association: {
id: ...
}
}
I'd like to collect objects with the same association id and get a array like this:
[ [ { ... association { id: 1} }, { ... association { id: 1} } ], [ { ... association { id: 2 } } ] ]
How can I do this?
Sounds like you're looking for a function that will return an array of objects that contain an association id that is provided
const data = [{...},{...},{...}]
const getByAssociationID = (source, id) => source.filter(obj => obj.association.id === id)
console.log(getByAssociationID(data, id))
This should group the data as you describe
function groupByAssociation(data) {
return data.reduce((list, value) => {
let added = false;
list.forEach(group => {
if(group[0].association.id === value.association.id) {
group.push(value);
added = true;
}
});
if(!added) {
list.push([ value ]);
}
return list;
}, []);
}
Use forEach to build and object with keys as association.id and values are accumulated.
const data = [
{
id: 1,
name: "blah",
association: {
id: "a1"
}
},
{
id: 2,
name: "foo",
association: {
id: "a2"
}
},
{
id: 3,
name: "test",
association: {
id: "a2"
}
}
];
const process = data => {
const obj = {};
data.forEach(item => {
const aId = item.association.id;
const newItem = obj[aId] || [];
newItem.push(item);
obj[aId] = newItem;
});
return Object.values(obj);
};
console.log(process(data));

Delete specific object with id

I have an array of objects, I need to delete a complete object based on the id
Input :
filters: [
{
key: "status",
label: "En attente",
value: "waiting",
id: 0
},
{
key: "dateDue[min]",
label: "15/12/2019",
value: "15/12/2019",
id: 1
},
{
key: "dateDue[max]",
label: "02/02/2020",
value: "02/02/2020",
id: 2
},
{
key: "bien",
values: [
{
label: "Studio Bordeaux",
value: 36,
id: 3
},
{
label: "Studio 2",
value: 34,
id: 184
}
]
},
{
key: "type",
values: [
{
type: "receipts",
label: "Loyer",
value: "loyer",
id: 4
},
{
type: "receipts",
label: "APL",
value: "apl",
id: 5
},
{
type: "spending",
label: "taxes",
value: "taxes",
id: 6
}
]
}
]
So I created a removeItem method with the id that must be deleted in parameters
removeItem method :
removeItem = (e, id) => {
const { filters } = this.state;
const remove = _.reject(filters, el => {
if (!_.isEmpty(el.values)) {
return el.values.find(o => o.id === id);
}
if (_.isEmpty(el.values)) {
return el.id === id;
}
});
this.setState({
filters: remove
});
};
I use lodash to make my job easier and more specifically _.reject
My issue is the following :
I manage to correctly delete the classic objects for example
{
key: "status",
label: "En attente",
value: "waiting",
id: 0
}
but my method however does not work for objects of the following form
{
key: "bien",
values: [
{
label: "Studio Bordeaux",
value: 36,
id: 3
},
{
label: "Studio 2",
value: 34,
id: 184
}
]
},
currently the whole object is deleted and not only the object in the values array according to its id
Here is my codesandbox!
thank you in advance for your help
EDIT
I found a solution with lodash (compact), I share my solution here :
removeIdFromCollection = id => {
const { filters } = this.state;
const newFilters = [];
_.map(filters, filter => {
if (filter.values) {
const valuesTmp = _.compact(
_.map(filter.values, value => {
if (value.id !== id) return value;
})
);
if (!_.isEmpty(valuesTmp)) {
return newFilters.push({
key: filter.key,
values: valuesTmp
});
}
}
if (filter.id && filter.id !== id) return newFilters.push(filter);
});
return newFilters;
};
removeItem = id => e =>
this.setState({
filters: this.removeIdFromCollection(id)
});
The values false, null, 0, "", undefined, and NaN are removed with lodash compact (_.compact(array))
Here is my updated codesandbox
You will need to filter the filters array and each values separately. Below is a recursive function which will remove items with the given id from the filters array and from the values property.
PS. This example is not using Lodash as I think it is not needed in this case.
removeIdFromCollection = (collection, id) => {
return collection.filter(datum => {
if (Array.isArray(datum.values)) {
datum.values = this.removeIdFromCollection(datum.values, id);
}
return datum.id !== id;
});
}
removeItem = (e, id) => {
const { filters } = this.state;
this.setState({
filters: this.removeIdFromCollection(filters, id),
});
};
The problem would be the structure of the object. You'll need to refactor for that inconvenient array out of nowhere for uniformity:
// Example
filters: [
...
{
key: "type",
values: [
{
type: "receipts",
label: "Loyer",
value: "loyer",
id: 4
},
...
]
...
}
// could be
filters: [
...
{
key: "type-receipts",
label: "Loyer",
value: "loyer",
id: 4
}
...
]
Repeat the pattern on all of it so you could just use the native array filter like this:
const newFilters = filters.filter(v => v.id !== id);
this.setState({
filters: newFilters,
});
I found a solution with lodash, I share it with you here :
removeIdFromCollection = id => {
const { filters } = this.state;
const newFilters = [];
_.map(filters, filter => {
if (filter.values) {
const valuesTmp = _.compact(
_.map(filter.values, value => {
if (value.id !== id) return value;
})
);
if (!_.isEmpty(valuesTmp)) {
return newFilters.push({
key: filter.key,
values: valuesTmp
});
}
}
if (filter.id && filter.id !== id) return newFilters.push(filter);
});
return newFilters;
};
removeItem = id => e =>
this.setState({
filters: this.removeIdFromCollection(id)
});
Here is my updated codesandbox

Add id:s for objs in arr. R.assoc('id', i++) not incrementing i

I am trying to add id:s to objects in an array with Ramda, but the id just equals 1 for every object.
let i = 1;
return R.evolve({
cms: {
components: R.map(R.assoc('id', i++)),
},
}, state),
I assume that has something to do with the i++. One is not supposed to mutate like that with Ramda.
But then, how would I do this correctly.
You have to wrap it in a function, otherwise i++ is evaluated once and then applied to all your elements.
const state = {
cms: {
components: [{
name: 'Serge'
}, {
name: 'Odile'
}, {
name: 'Simon'
}, {
name: 'Émile'
}]
}
};
let i = 1,
modifiedState = R.evolve({
cms: {
components: R.map((element) => R.assoc('id', i++, element)),
},
}, state);
console.log(modifiedState.cms.components);
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>
Or you could use Ramda's addIndex like this:
const transform = R.evolve({
cms: {
components: R.addIndex(R.map)((comp, i) => R.assoc('id', i + 1, comp)),
},
})
const state = {
cms: {
components: [
{name: 'Serge'},
{name: 'Odile'},
{name: 'Simon'},
{name: 'Émile'}
]
}
};
console.log(transform(state))
<script src="https://cdnjs.cloudflare.com/ajax/libs/ramda/0.25.0/ramda.min.js"></script>

Categories

Resources