Update a nested state in redux - javascript

I have an object in my state as follows:
Exercise: {
id: 1,
question: '',
type: '',
Groups: [
{
id: 1,
category: {
id: 1,
value: 'xxx',
color: 'xxx'
},
groupParts: [
{
id: 1,
Index: 7
},
{
id: 2,
Index: 11
}
]
}
]
}
How can I update the value of the Index in id:2 in the reducer?
this is my last try which does not update the value, but creates another section in the current state:
case CURRENT_WORD_INDEX_UPDATED:
const index=action.selectedWordIndex
return{...state,index:{...state.Groups[0].groupParts[1].index,in‌​dex},}

you can make use of immutability-helper to update a nested state
import update from 'immutability-helper';
......
case CURRENT_WORD_INDEX_UPDATED:
const index=action.selectedWordIndex
return update(state, {
Groups: {
0: {
groupParts: {
0: {
Index: {
$set: index
}
}
}
}
}
})

Related

Map an object of properties where some properties do not exist

I currently am utilising Array.map to create a new object containing some data:
const bookList = [{
name: "Foo",
id: "1234",
quantity: 5,
}];
function mapBooks(bookList) {
return {
eventName: "ping",
data: {
list: {
books:
bookList.map(
({name, id, quantity }) => ({ name, id, quantity})
)
}
}
};
}
mapBooks(bookList);
// Result:
{
eventName: "ping",
data: {
list: {
books: {
name: "Foo",
id: "1234",
quantity: 5,
}
}
},
}
This is fine in this example, but what happens when one of the items is not in the provided data?
const bookList = [{
name: "Foo",
id: "1234",
}];
mapBooks(bookList);
// Result:
{
eventName: "ping",
data: {
list: {
books: {
name: "Foo",
id: "1234",
quantity: undefined,
}
}
},
}
How can I adjust my map function to simply not return any undefined values? For example I would prefer a result like this:
mapBooks(bookList);
// Result:
{
eventName: "ping",
data: {
list: {
books: {
name: "Foo",
id: "1234",
// quantity is simply not included
}
}
},
}
I don't know if i understand the question correctly but you could something like this:
const bookList = {
name: "Foo",
id: "1234",
quantity: 5,
};
function mapBooks(bookList) {
return {
eventName: "ping",
data: {
list: {
books:
bookList.map(
(book) => ({ ...book})
)
}
}
};
}
With the power of destructuring you will only fulled the present option of the object

Group array of objects by multiple nested values

I have an array of objects which presents tasks. These tasks are categorized (primary / secondary category).
let tasks = [
{
id: 1,
name: 'Cleanup desk',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 2,
name: 'Surfaces'
}
},
{
id: 2,
name: 'Cleanup office floors',
primary_category: {
id: 1,
name: 'Indoor'
},
secondary_category: {
id: 3,
name: 'Ground'
}
},
{
id: 3,
name: 'Water plants',
primary_category: {
id: 2,
name: 'Outdoor'
},
secondary_category: {
id: 3,
name: 'Irrigation'
}
}
];
I now try to create a categories accordion in my frontend and therefore need to group my array differently. The structure should look like:
1) primary category
> secondary category
> tasks
> secondary category
> tasks
2) primary category
> secondary category
> tasks
Therefore I'm trying to achieve a structure similar to this:
let tasks_categorized = [
{
id: 1,
name: 'Indoor',
secondary_categories: [
{
id: 2,
name: 'Surfaces',
tasks: [
{
id: 1,
name: 'Cleanup desk'
}
]
},
{
id: 3,
name: 'Ground',
tasks: [
{
id: 2,
name: 'Cleanup office floors'
}
]
}
]
},
{
id: 2,
name: 'Outdoor',
secondary_categories: [
{
id: 3,
name: 'Irrigation',
tasks: [
{
id: 3,
name: 'Water plants'
}
]
}
]
}
];
I tried using groupBy by lodash but this does not allow grouping by multiple nested key-value pairs. Does anybody know an approach to solve this?
Thank you in advance!
The following provided approach is going to achieve the expected result within a single reduce cycle without any further nested loops.
It does so by implementing a reducer function which creates and/or aggregates at time a prioritized category task while iterating another task array. But most importantly it keeps track of a task item's related primary and secondary categories via a Map based lookup. This lookup reference together with a result array are properties of this function's return value which has to be partly provided as the reduce method's initial value as follows ... { result: [] }.
function createAndAggregatePrioritizedCategoryTask(
{ lookup = new Map, result }, item
) {
const { primary_category, secondary_category, ...taskRest } = item;
const { id: primaryId, name: primaryName } = primary_category;
const { id: secondaryId, name: secondaryName } = secondary_category;
const primaryKey = [primaryId, primaryName].join('###');
const secondaryKey = [primaryKey, secondaryId, secondaryName].join('###');
let primaryCategory = lookup.get(primaryKey);
if (!primaryCategory) {
// create new primary category item.
primaryCategory = {
id: primaryId,
name: primaryName,
secondary_categories: [],
};
// store newly created primary category reference in `lookup`.
lookup.set(primaryKey, primaryCategory);
// push newly created primary category reference to `result`.
result.push(primaryCategory);
}
let secondaryCategory = lookup.get(secondaryKey);
if (!secondaryCategory) {
// create new secondary category item.
secondaryCategory = {
id: secondaryId,
name: secondaryName,
tasks: [],
};
// store newly created secondary category reference in `lookup`.
lookup.set(secondaryKey, secondaryCategory);
// push newly created secondary category reference into the
// `secondary_categories` array of its related primary category.
primaryCategory
.secondary_categories
.push(secondaryCategory);
}
// push the currently processed task-item's rest-data as
// item into the related secondary category's `task` array.
secondaryCategory
.tasks
.push(taskRest);
return { lookup, result };
}
let tasks = [{
id: 1,
name: 'Cleanup desk',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 2, name: 'Surfaces' },
}, {
id: 2,
name: 'Cleanup office floors',
primary_category: { id: 1, name: 'Indoor' },
secondary_category: { id: 3, name: 'Ground' },
}, {
id: 3,
name: 'Water plants',
primary_category: { id: 2, name: 'Outdoor' },
secondary_category: { id: 3, name: 'Irrigation' },
}];
const { result: tasks_categorized } = tasks
.reduce(createAndAggregatePrioritizedCategoryTask, { result: [] });
console.log({ tasks_categorized });
.as-console-wrapper { min-height: 100%!important; top: 0; }
You could take a dynamic approach with an array of arrays with functions and keys for the nested arrays.
const
tasks = [{ id: 1, name: 'Cleanup desk', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 2, name: 'Surfaces' } }, { id: 2, name: 'Cleanup office floors', primary_category: { id: 1, name: 'Indoor' }, secondary_category: { id: 3, name: 'Ground' } }, { id: 3, name: 'Water plants', primary_category: { id: 2, name: 'Outdoor' }, secondary_category: { id: 3, name: 'Irrigation' } }],
groups = [
[o => o, 'primary category'],
[o => o.primary_category, 'secondary category'],
[o => o.secondary_category, 'tasks']
],
result = tasks.reduce((r, o) => {
groups.reduce((parent, [fn, children]) => {
const { id, name } = fn(o);
let item = (parent[children] ??= []).find(q => q.id === id)
if (!item) parent[children].push(item = { id, name });
return item;
}, r);
return r;
}, {})[groups[0][1]];
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Rename result property in normalizr

Given data like:
{
id: 1,
ownerName: 'bob',
devices: [
{
id: 2
},
{
id: 3
}
]
}
how would I convert it to the following object
{
result: 1,
entities: {
owners: {
1: {
id: 1,
ownerName: 'bob',
deviceIds: [2, 3]
}
},
devices: {
2: {
id: 2
},
3: {
id: 3
}
}
}
}
using normalizr? I can't figure out how to change devices to deviceIds in the returned result...
You can use the Process Strategy for this. It allows you to manipulate your data before it is processed. Simply return a copy of your object with the keys changed from the processStrategy() method.
const Device = schema.Entity('devices');
const Owner = schema.Entity(
'owners',
{
deviceIds: [ Device ]
},
{
processStrategy: value => ({
id: value.id,
ownerName: value.ownerName,
deviceIds: value.devices
})
}
);

ReactJS - Swap out all the values in an array of objects in state with another array

I am trying to swap out all the values of an array of objects in state with a whole new array of objects. However, nothing seems to be working. I've tried the following:
const list1 = [
{ id: 1, name: 'item1' },
{ id: 2, name: 'item1' },
{ id: 3, name: 'item1' },
{ id: 4, name: 'item1' },
]
const list2 = [
{ id: 1, name: 'newItem1' },
{ id: 2, name: 'newItem2' },
{ id: 3, name: 'newItem3' },
{ id: 4, name: 'newItem4' },
]
class FindTab extends Component {
state = {
status: 'loading',
location: null,
view: this.props.view,
map: this.props.map,
locationValues: list1,
}
}
this.setState(prevState => ({
locationValues: [ ...prevState.locationValues, list2 ],
}))
or just simpler:
this.setState(locationValues: list2)
Neither seem to work. Is there any guidance as to how one should replace an array of objects with another array for a state property?
You could spread the array in a new one like:
const locationValues = [ ...state.locationValues, ...list2 ]
this.setState({ locationValues })

Using spread syntax in Reducer

I am trying to use the spread syntax to update the state inside the reducer
The state consists of an object, and the object has an array,
I would like to update all properties in the object, except for the array, which I would like to add the next state elements at the end. For example,
For example, if the state is
{
id: 4,
amount: 10,
arr: [
name: "peter",
name: "john"
]
}
and the action
{
id: 7,
amount: 7,
arr: [
name: "sally",
name: "maria"
]
}
I would like to get as a result of using the spread syntax
{
id: 7,
amount: 7,
arr: [
name: "peter",
name: "john",
name: "sally",
name: "maria"
]
}
taking the id and amount of the action, and concatenating the array
Thank you
Simply keep spreading props.
spread your current state
spread action's payload
change properties as needed
const INITIAL_STATE = {
id: 4,
amount: 10,
arr: [
{ name: "peter" },
{ name: "john" },
],
}
function reducer(state = INITIAL_STATE, action) {
switch (action.type) {
default: return state
case 'ANY_ACTION':
return {
...state,
...action.payload,
arr: [
...(state.arr || []),
...action.payload.arr,
]
}
}
}
const action = {
type: 'ANY_ACTION',
payload: {
id: 7,
amount: 7,
arr: [
{ name: "sally" },
{ name: "maria" },
]
}
}
const state = reducer(undefined, action)
console.log(state)
First of all your structure is invalid, the correct structure would look like
{
id: 4,
amount: 10
data: [
{name: "peter"},
{name: "john"}
]
}
and then you could use spread operator to update state assuming action.payload to be
{
id: 7,
amount: 7
data: [
{name: "sally"},
{name: "maria"}
]
}
like
case 'WHATEVER_ACTION':
return {
...state,
id: action.payload.id,
amount: action.payload.amount,
data: [...state.data, ...action.payload.data]
}
Check the documentation of Spread syntax to understand its usage more

Categories

Resources