I have a svelte store that uses this data:
{ "books": [
{
"id": "F0tE_25",
"title": "Abc",
...
},
"id": "zNPAQit",
"title": "Ny, Ny",
...
}
]
}
I edit a book in a form and call the function below to update the store (id is the book id to update and bookData is the updated data (from the form):
updateBook: (id, bookData) => {
bookstore.update(items => {
const index = items.findIndex(i => i.id ===id)
const updatedBook = {...items[index], ...bookData}
const updatedBooks = [...items]
updatedBooks[index] = updatedBook
return updatedBooks
})
}
It works. It just seems like a lot of juggling to perform an update. Wondered if there was a better way?
There are a few problems associated with the approach in the question viz use of findIndex. If the item with the given id doesn't exist the value of index will be -1 which will result items[index] to be undefined and will be the cause of other undesired behavior.
You can use .map() to eliminate and simplify the update. Here is an example.
updateBook: (id, bookData) => {
bookstore.update(items =>
items.map(item => {
if (item.id === id) {
return { ...item, ...bookData };
}
return item;
})
);
}
Edited: #pilchard's answer might be even clearer implementation with the use of ternary operator.
updateBook: (id, bookData) => {
bookstore.update(items =>
items.map(item => {
return item.id === id ? { ...item, ...bookData } : item;
})
);
}
Hope that helps.
Related
i have a issue, i have two inputs then and i can't permit the user save this edit if the two values is equals.
my state who contains a data from db
const [doctorRegisters, setDoctorResgisters] = useState<any>([]);
inside this array i contain this
[{"__typename": "DoctorMedicalRegister", "counsil": "CRM", "id": "141", "isMainRegister": true, "register": "1234567/RS"}, {"__typename": "DoctorMedicalRegister", "counsil": "CRM", "id": "153", "isMainRegister": false, "register": "1234567/RS"}]
and i need compare two register who if is equal, i cant permit user save he save just is different
here is a code who i try fix this
const isEquals = () => {
doctorRegisters.map((item: any) => {
if (item.register) {
doctorRegisters.map((item2: any) => {
if (item2.register) {
if (item.register === item2.register) {
console.log('iguais')
}
}
});
}
});
};
but this work only for dont edited values i need verify when this value is changed in input in this case i only verify in db this values is equals
here is my handle change
const handleEditRegisterCrm = (crm: string, id: number) => {
setDoctorResgisters(
doctorRegisters.map((item: any) => {
if (item && Number(item.id) == id) {
item.register = `${crm}/${item.register?.split('/')[1] || ''}`;
}
return item;
}),
);
};
You could do something like:
const handleEditRegisterCrm = (crm: string, id: number) => {
if (!doctorRegisters.some((doctorRegister) => doctorRegister.register.includes(registerToCompare)) {
setDoctorRegisters(
doctorRegisters.map((item: any) => {
if (item && Number(item.id) == id) {
item.register = `${crm}/${item.register?.split('/')[1] || ''}`;
}
return item;
}),
);
}
};
Remember you should keep track of the registerToCompare in order to find if it's already inserted in the doctorRegisters list. I'm assuming you can obtain that value from the your handleChange function.
The idea was to query a dataset with querystring params. I only want the "records" to match only what was queried.
Dataset
{
1111:
{
Category: "Education"
Role: "Analyst"
}
2222:
{
Category: "Communications and Media"
Role: "Analyst"
}
3333:
{
Category: "Public Sector"
Role: "Something else"
}
4444:
{
Category: "Public Sector"
Role: "Something else"
}
...
}
[[Prototype]]: Object
I'm sending in qString
Category: (2) ['Communications and Media', 'Education']
Role: ['Analyst']
length: 0
[[Prototype]]: Array(0)
I'd like to loop over that and filter/reduce so I only have records that match. Sort of an and instead of an or.
dataSet is an Object of objects. Thoughts? Thanks in advance.
export const Filtered = (qStrings, dataSet) => {
const filtered = [];
Object.entries(qStrings).forEach(([field]) => {
qStrings[field].forEach((value) => {
filtered.push(
..._.filter(dataSet, (sess) => {
if (sess[field] && sess[field].toString() === value.toString()) {
return sess;
}
})
);
});
});
return _.uniq(filtered);
};
geez, I figured it out with a colleague who's way smarter than me wink Jess!
export const Filtered = (qStrings, dataSet) => {
let filtered = [];
Object.entries(qStrings).forEach(([field], idx) => {
let source = filtered;
if (idx === 0) {
source = dataSet;
}
filtered = _.filter(source, (sess) => {
return sess[field] && sess[field].includes(qStrings[field]);
});
});
return _.uniq(filtered);
};
Now to clean this up.
Not sure if this solves your problem exactly, but you can apply this logic without mutation for a much cleaner function.
export const matches = (qStrings, dataSet) =>
Object.entries(dataSet).reduce((acc, [key, value]) =>
Object.entries(value).every(([rKey, rValue]) => qStrings[rKey]?.includes(rValue))
? { ...acc, [key]: value }
: acc,
{});
This will return records 1111 and 2222 because they match one of the categories and the role in qStrings.
I am trying to implement a functionality where I want to push object into an array when the user checked it and remove it if the user unselects it. I have a menu where I am collecting user choices. I have implemented code but it has not resolved my issue, Could someone please help me how to resolve this issue. Thanks
const selectSingle = (id, item, index) => {
const user = Cookies.get("user") && JSON.parse(Cookies.get("user"));
let callScheduleIds = Object.assign([], allCallNotifications);
if (callScheduleIds.findIndex((item) => item.order_id === id)) {
callScheduleIds.push({
order_id: id,
phone_number: item.phone1,
sender_id: user.user.id,
});
} else {
callScheduleIds.splice(callScheduleIds.indexOf(id), 1);
}
setAllCallNotifications(callScheduleIds);
};
You can do it by using Lodash.
const selectSingle = (rowIndex, item) => {
let callIds = Object.assign([], allCallNotifications)
if (_.findIndex(callIds, { id: item.id }) === -1) {
callIds.push(item)
} else {
callIds.splice(_.findIndex(callIds, { id: item.id }), 1)
}
setAllCallNotifications(callIds)
}
The relevant Redux state consists of an array of objects representing layers.
Example:
let state = [
{ id: 1 }, { id: 2 }, { id: 3 }
]
I have a Redux action called moveLayerIndex:
actions.js
export const moveLayerIndex = (id, destinationIndex) => ({
type: MOVE_LAYER_INDEX,
id,
destinationIndex
})
I would like the reducer to handle the action by swapping the position of the elements in the array.
reducers/layers.js
const layers = (state=[], action) => {
switch(action.type) {
case 'MOVE_LAYER_INDEX':
/* What should I put here to make the below test pass */
default:
return state
}
}
The test verifies that a the Redux reducer swaps an array's elements in immutable fashion.
Deep-freeze is used to check the initial state is not mutated in any way.
How do I make this test pass?
test/reducers/index.js
import { expect } from 'chai'
import deepFreeze from'deep-freeze'
const id=1
const destinationIndex=1
it('move position of layer', () => {
const action = actions.moveLayerIndex(id, destinationIndex)
const initialState = [
{
id: 1
},
{
id: 2
},
{
id: 3
}
]
const expectedState = [
{
id: 2
},
{
id: 1
},
{
id: 3
}
]
deepFreeze(initialState)
expect(layers(initialState, action)).to.eql(expectedState)
})
One of the key ideas of immutable updates is that while you should never directly modify the original items, it's okay to make a copy and mutate the copy before returning it.
With that in mind, this function should do what you want:
function immutablySwapItems(items, firstIndex, secondIndex) {
// Constant reference - we can still modify the array itself
const results= items.slice();
const firstItem = items[firstIndex];
results[firstIndex] = items[secondIndex];
results[secondIndex] = firstItem;
return results;
}
I wrote a section for the Redux docs called Structuring Reducers - Immutable Update Patterns which gives examples of some related ways to update data.
You could use map function to make a swap:
function immutablySwapItems(items, firstIndex, secondIndex) {
return items.map(function(element, index) {
if (index === firstIndex) return items[secondIndex];
else if (index === secondIndex) return items[firstIndex];
else return element;
}
}
In ES2015 style:
const immutablySwapItems = (items, firstIndex, secondIndex) =>
items.map(
(element, index) =>
index === firstIndex
? items[secondIndex]
: index === secondIndex
? items[firstIndex]
: element
)
There is nothing wrong with the other two answers, but I think there is even a simpler way to do it with ES6.
const state = [{
id: 1
}, {
id: 2
}, {
id: 3
}];
const immutableSwap = (items, firstIndex, secondIndex) => {
const result = [...items];
[result[firstIndex], result[secondIndex]] = [result[secondIndex], result[firstIndex]];
return result;
}
const swapped = immutableSwap(state, 2, 0);
console.log("Swapped:", swapped);
console.log("Original:", state);
I have an Immutable.js collection that looks like this:
{ groups: Immutable.Map({
{
id: 1,
members: Immutable.List
},{
id: 2,
members: Immutable.List
}
})
}
I'm getting an updated members array and the group id from my server. How can I find the correct group by id and then update the members prop and then return a new groups collection?
I have something like this so far that doesn't work.
return state.update('groups', groups => {
for (let group of groups) {
if (group.id === action.id) {
group.members = Immutable.fromJS(action.members);
// not sure what I would do with updated here
//const updated = group.members.set('members', action.members);
}
}
return groups;
};
Also, tried this and it doesn't seem to complete, because the console.log never fires.
let update = state.get('groups')
.find(group => group.id === action.id)
.set('members', action.members)
console.log(update) // nothing, no error either
The following works, but there has to be a better, more elegant way than using fromJS and then toJS.
return state.update('groups', groupsImmutable => {
const groups = groupsImmutable.toJS();
for (let group of groups) {
if (group.id === activeId) {
group.members = action.members;
break;
}
}
return Immutable.fromJS(groups);
});
thanks!
This would work:
return state.update('groups', groups => {
return groups.map(group => {
if (group.get('id') === action.id) {
return group.set('members', Immutable.fromJS(action.members))
} else {
return group
}
})
})
You could also use findIndex to get the index of the matching group id and then use setIn to update the members array:
const updateIndex = state.get('groups').findIndex(group => group.get('id') === action.id);
if (updateIndex === -1) { // action.id not found
return state;
} else {
return state.setIn(['groups',updateIndex,'members'], Immutable.fromJS(action.members))
}
Your examples failed because you were trying to directly modify a child element, that's not how Immutable works. You need to update the entire state object with the new element.