I know there is a lot of similar threads but I didn't find a solution there.
Okay so I have a state which looks:
[
{
id: 1,
title: 'todo',
items: [
{
itemID: 1,
itemText: 'finish this project'
},
{
itemID: 2,
itemText: 'take trash out'
},
{
itemID: 3,
itemText: 'sleep'
},
]
},
{
id: 2,
title: 'doing now',
items: [
{
itemID: 1,
itemText: 'add redux'
},
{
itemID: 2,
itemText: 'fixing bugs'
},
]
},
{
id: 3,
title: 'done',
items: [
{
itemID: 1,
itemText: 'add components'
},
{
itemID: 2,
itemText: 'add material ui'
},
]
}
]
and I have a redux reducer that should add an item into specific list, I have id of that list, and itemText which I should add. I managed somehow to make it kinda work, it is adding item into specifin array but problem is that also creates new blank list. Here is how reducer looks:
case "ADD_ITEM":
return [
state.map((list)=>(
(list.id === action.payload.id) ? list.items.push({
itemID: 89,
itemText: action.payload.description
}) : list
))
]
Any idea how to add an item to specific list items without creating a new list?
Map and return the lists (state), and use spread syntax to update the items:
case "ADD_ITEM":
return state.map(list =>
list.id === action.payload.id ? {
...list,
items: [
...list.items,
{
itemID: 89,
itemText: action.payload.description
}
]
} : list
)
Try this:
case "ADD_ITEM":
state.forEach((list, index)=>(
if (list.id === action.payload.id){
state[index].items.push({
itemID: 89,
itemText: action.payload.description
}
})
))
Note that this is pseudocode, but it should still work.
Your example was not working because you were returning a new array, which had (about) the same structure as state, except with the added element. To add it, simply add it directly to state without using map + return, thereby not creating a new array.
Related
I am trying to create a component that calls upon two arrays. The arrays are them mapped out and the pair that matches is send down through props into a child component. I am making a list component that calls said arrays from the back end and then sends down the information to a listItem component to be rendered. The arrays look something like this
const Stores = [
{
id: 1,
name: Store One,
},
{
id: 2,
name: Store Two,
},
]
const Users = [
{
id: 100,
name: User One,
storeId: 1,
},
{
id: 200,
name: User Two,
storeId: 1,
},
{
id: 300,
name: User Three,
storeId: 2,
},
{
id: 400,
name: User Four,
storeId: 2,
},
]
Currently i am just passing down the user information into the listItem component and using http requests on each item but as you can tell when the User or Stores list reaches 100+ items that the amount of http requests will bog down the app too much. What I thought of is just calling once in the List component and passing down each information that way it would be one call but cannot figure out the function required to acheive this. Is there anyone that can help me brainstorm?
const Stores = [{
id: 1,
name: 'Store One',
},
{
id: 2,
name: 'Store Two',
},
]
const Users = [{
id: 100,
name: 'User One',
storeId: 1,
},
{
id: 200,
name: 'User Two',
storeId: 1,
},
{
id: 300,
name: 'User Three',
storeId: 2,
},
{
id: 400,
name: 'User Four',
storeId: 2,
},
]
const grouped = Users.map(user => {
// find store
const store = Stores.find(store => store.id === user.storeId)
// return grouped
return {
user: { ...user },
store: { ...store }
}
})
console.log(grouped)
I have this data below.
I need to be able to search in the objet for the id or name key and then change the 'show' key to a different value.
How example:
Search the data for id value 2 and change the show value to false.
data = [
{
id: 1,
name: 'one',
show: false;
title: 'title1',
data: [
{
id: 1,
description: 'some description'
},
{
id: 2,
description: 'some other description'
}
]
},
{
id: 2,
name: 'two',
show: true;
title: 'title2',
data: [
{
id: 1,
description: 'some description'
},
{
id: 2,
description: 'some other description'
}
]
}
]
How can I do this?
You can use the findIndex method, and then access your array using the found Index and change any property you want, here's some code that match your use case
let index = data.findIndex((x) => x.id == THE_ID_YOU_ARE_LOOKING_FOR);
if(index > -1) {
data[index].show = THE_VALUE_YOU_WANT;
}
You can also use normal array find method.
let item = data.find(x=>x.id===REQUIRED_ID);
if(item) item.show = false
There is an array of objects
const groups = [
{ id: 0, name: "All", selected: false },
{ id: -1, name: "All", selected: true },
{ id: 1, name: "Group1", selected: false },
{ id: 2, name: "Group2", selected: false },
{ id: 3, name: "Group3", selected: false },
{ id: 4, name: "Group4", selected: true }
];
I want to extract ids from this object with map
groups.map(group => group.id > 0 && group.selected ? group.id:null)
but the result will be
[null,null,4,null...]
actually it should be [4]
I know I can use another function like forEach and push or map and filter but I would solve it with one iteration with map or something else.
Filter the object/s under your criteria and then extract the id/s with a map
const groups = [{
id: 0,
name: "All",
selected: false
},
{
id: -1,
name: "All",
selected: true
},
{
id: 1,
name: "Group1",
selected: false
},
{
id: 2,
name: "Group2",
selected: false
},
{
id: 3,
name: "Group3",
selected: false
},
{
id: 4,
name: "Group4",
selected: true
}
];
const result = groups.filter(x => x.id > 0 && x.selected).map(x => x.id)
console.log(result)
you can use a transducer in this case, so that you will not iterate through the array 2 times.
const groups = [
{ id: 0, name: "All", selected: false },
{ id: -1, name: "All", selected: true },
{ id: 1, name: "Group1", selected: false },
{ id: 2, name: "Group2", selected: false },
{ id: 3, name: "Group3", selected: false },
{ id: 4, name: "Group4", selected: true }
];
const filteredIds = groups.reduce(
(ids, { id, selected }) => (
id > 0 && selected ? [...ids, id] : ids
), []
);
console.log(filteredIds);
The map() method creates a new array with the results of calling a function for every array element and extraction is not possible with this. Either use map() and then discard the array items or use filter().
Better approach would be using filter(). The filter() method creates an array filled with all array elements that pass a test (provided as a function).
let result = groups.filter(x => x.id > 0 && x.selected).map(x => x.id)
You can easily do this in one iteration with transducers.
const getPositiveSelectedIDs = pipe([
filter(and([
gt(get('id'), 0),
get('selected'),
])),
map(get('id')),
])
transform(getPositiveSelectedIDs, [])(groups) // => [4]
In this example, getPositiveSelectedIDs is a transducer we declared using functions pipe, map, and filter. The predicate passed to filter uses functions and, gt, and get to say
only let groups through who have positive ids and who have been selected
then, without creating any intermediate arrays, we get the ids of each group with map and get. getPositiveSelectedIDs behaves as a transducer when we use it in transform. The flow of data starts when we call the transformation transform(getPositiveSelectedIDs, []) with groups.
More on transducers
I am retrieving a sorted array of comments from the server. Each comment has a comment.children attribute that is also a sorted array of other comments. These can be nested n deep. E.g:
const nestedComments = [
{
id: 1,
body: "parent comment",
children: [
{
id: 4,
body: 'child comment',
children: [
{
id: 7,
body: 'grandchild comment #1',
children: [],
},
{
id: 6,
body: 'grandchild comment #2',
children: [],
},
{
id: 5,
body: 'grandchild comment #3',
children: [],
},
]
},
{
id: 3,
body: 'second child comment',
children: []
}
],
},
{
id: 8,
body: 'parent comment #2',
children: []
},
];
Then I use the normalizr library to normalize it like this:
const commentSchema = new schema.Entity('comments');
const commentListSchema = new schema.Array(commentSchema);
commentSchema.define({children: commentListSchema});
const normalizedComments = normalize(nestedComments, commentListSchema);
The result is pretty much as expected:
{
entities: {
// All of the comments ordered their id's, this is fine and what I want
},
results: [1,8] // understandable but not what I want
}
So it preserves the order of the root comments but doesn't do anything for the nested child comments. Is there a way to do this so that each group of siblings has its own results array? Something that looks like this:
results: [[1,8], [4,3], [7,6,5]];
Or maybe there is a better way to preserve that information and I'd appreciate hearing about that as well.
The answer that will work for me was already present in the results. The comments in the entities stores an array of the children ids that is in the original order.
entities: {
{
id: 1,
body: 'parent comment',
children: [4,3]
},
...
{
id: 4,
body: 'child comment',
children: [7,6,5],
},
...
}
I am trying to implement a grid with grouping feature. This is my Model:
Model
Ext.define('myApp.model.ModelPrueba2', {
extend: 'Ext.data.Model',
fields: ['id', {
name: 'name',
type: 'string'
},'sign']
});
I fill my store from an AJAX proxy request. This is my JSON:
{
success: true,
data: [{
id: 1,
name: 'Name 1',
sign: {
id: 1,
name: 'F1'
}
}, {
id: 2,
name: 'Name 2',
sign: {
id: 2,
name: 'F2'
}
}]
}
You can appreciate that my 'sign' field is an 'object'. My problem is: when I group by 'name', it works fine, and if I collapse some group, it only collapses the group that I selected. However, if I group by 'sign', the groups are well-formed and displayed, but when I collapse a specific group, all the groups get collapsed, and the only difference with the previous test is the field type, 'name' is string and 'sign' is an object... So I don't really know if this a bug. I am using 4.2 version.
This is a fiddle where you can test my problem: https://fiddle.sencha.com/#fiddle/vt
Here you can group by 'probabilities to win', and collapse one group, it works fine, but if you delete every
toString: function() {
return this.value;
}
from the 'data' array and do the same, you will see how all groups get collapsed in one. What can I do to fix this?, I don't like to use this toString() in my store or JSON.
How can I group by an Object attribute?
you MAY have some luck with a "hasOne" relationship in your model instead of the "auto' type. (I'm not sure if it's possible to group using a relationship)
Or you could create another field just for the grouping (type string or int) like this :
{
success: true,
data: [{
id: 1,
name: 'Name 1',
myGroupField : 1,
sign: {
id: 1,
name: 'F1'
}
}, {
id: 2,
name: 'Name 2',
myGroupField : 2,
sign: {
id: 2,
name: 'F2'
}
}]
}
A similar solution is to bring up the field from your nested object to the parent object :
{
success: true,
data: [{
id: 1,
name: 'Name 1',
signId: 1,
signName: 'F1'
}, {
id: 2,
name: 'Name 2',
signId: 2,
signName: 'F2'
}]
}
and then group by signId