I have the following array in my React naitve app.
const [items, setItems] = useState([
{ answers: [{ id: 1 }, { id: 2 }] },
{ answers: [{ id: 1 }] },
{ answers: [{ id: 1 },] },
]);
I want to delete item from first row with id 2 So the final should look like that
[
{ answers: [{ id: 1 }] },
{ answers: [{ id: 1 }] },
{ answers: [{ id: 1 }] },
]
How can i do that ?
I tried to start like this
const onDelete = useCallback((itemId) => {
var newItems = [...items];
newItems = newItems[0].answers.filter(....) //I don't know how to continue
setItems(newItems);
}, []);
Sorry for the question but I'm new to react-native and javascript!
Assuming that you want to change the first object and you want do dynamically filter the answers based on your itemId, the onDelete function would look like this:
const onDelete = useCallback((itemId) => {
setItems((prev) => [
{ answers: prev[0].answers.filter(({ id }) => id !== itemId) },
...prev.slice(1),
]);
}, []);
I've expanded #sm3sher answer to allow removing answer ids at any index.
const onDelete = useCallback((answerId=2,itemIndex=0)=>{
const newItems = [...items];
let currentItem = newItems[itemIndex]
currentItem.answers = currentItem.answers.filter(({id})=> id !== answerId)
newItems[itemIndex] = currentItem;
setItems(newItems);
},[]);
You should create a new object, whose answers property is the filtered original. The filter callback can be ({id}) => id != 2:
newItems[0] = { answers: newItems[0].answers.filter(({id}) => id != 2) };
newItems = newItems.map(item => { return item.answers.filter(answer => answer.id !== 2)})
Related
I have an array of objects:
const users = [
{
name:John,
id:1
},
{
name:Dude,
id:2
}
]
And an array of ids:
const ID = [1,2,5]
I would like to to know that 5 is the missing ID so I can fetch that ID.
I have tried nesting for loops
users.forEach((user, index) => {
let userExists = false;
ID.forEach((id) => {
if (user.id == id) {
userExists = true;
return;
}
});
if (!userExists) {
console.log('user doesnt exists');
}
});
There might be a simple solution but currently I can't wrap my head around it
Thanks!
Its very easy to do that, use filter()
const users = [
{
name:"John",
id:1
},
{
name:"Dude",
id:2
}
]
const ID = [1,2,5]
console.log(ID.filter(x=> !users.find(f=> f.id == x)));
Modifying ID in place, using Array#forEach to iterate through each user and Array#splice to remove an item from ID.
const users = [ { name: 'John', id: 1 }, { name: 'Dude', id: 2 }, ];
const ID = [1, 2, 5];
users.forEach(({ id }) => ID.splice(ID.indexOf(id), 1));
console.log(ID);
I have a nested array object
const product = [{
ref_id: 'B123',
items: [
{
name: 'Brush',
ref_id: 'M1'
},
{
name: 'Paste',
ref_id: 'M2'
}
]
},
{
ref_id: 'B124',
items: [
{
name: 'Apple',
ref_id: 'M9'
},
{
name: 'Orange',
ref_id: 'M3'
}
]
},
{
ref_id: 'B113',
items: [
{
name: 'Maggi',
ref_id: 'M7'
},
{
name: 'Wai Wai',
ref_id: 'M12'
}
]
}
]
The outer ref_id is the category id and the inner one is the product id. Now, i have two array - one with values as category's ref_id and other with product's ref_id.
const filteredCategories = ['B123'];
const filteredItems = ['M2', 'M9', 'M7']
I want to filter out all items that fall under the filteredCategories ref_id and also under filteredItems ref_id which can be part of any other category. Also, the result should not contain duplicate items.
My Solution
let filteredProducts = [];
products.forEach(item => {
if(_.includes(filteredCategories, item.ref_id)) {
filteredProducts.push(...item.items);
} else {
const filterProductItems = _.filter(item.items, (productItem)=> _.includes(filteredItems, productItem.ref_id));
if(filterProductItems) {
filteredProducts.push(...filterProductItems);
}
}
});
Can this be done in a better and optimized way as items list can be really large?
#alia, I would suggest you use find method on product nested array object. Check for below code:
useEffect(() => {
getFilteredCategories()
}, []);
const filteredCategories = ['B123'];
let filteredProducts = [];
const getFilteredCategories = async () => {
filteredCategories.map((id, index) => {
const participants = product.find(o => o.ref_id === id);
filteredProducts.push(participants)
return filteredProducts;
});
console.log('filteredProducts', filteredProducts)
}
I am trying to update one value is_deleted in an array of objects for the deleted object.
const handleDelete = (idx) => {
const filteredCards = cards.map((card, i) => {
if(i == idx) {
card.is_deleted = true;
return {...cards}
}
})
setCards(filteredCards);
}
Output
cards = [
undefined,
[
{ id: 1, is_deleted: false },
{ id: 2, is_deleted: true },
]
]
Desired Output
cards = [
{ id: 1, is_deleted: false },
{ id: 2, is_deleted: true },
]
The output is partially right - at index 1 I see the two cards, with the second is_deleted being set to true. However, at index 0 there is an "undefined".
Any insight into why this is would be helpful! Thank you and let me know if I missed a key piece of info.
You could just access the card directly, rather than using map:
let cards = [
{ id: 1, is_deleted: false },
{ id: 2, is_deleted: false },
]
const setCards = c => cards = c(cards.slice())
const handleDelete = idx => setCards(cards => (cards[idx].is_deleted = true, cards))
handleDelete(1)
console.log(cards)
The setCards function is just mimicking react's useState.
I am trying to filter the parent, by removing it's child id only by not matching. in case if there is no child exist, the parent should be removed.
I try like this, but not works.
var rm = 7;
var objects = [
{
name: "parent1",
id: 1,
blog: [
{
name: "child1",
id: 1
},
{
name: "child2",
id: 2
}
]
},
{
name: "parent2",
id: 2,
blog: [
{
name: "child3",
id: 3
},
{
name: "child4",
id: 4
}
]
},
{
name: "parent3",
id: 3,
blog: [
{
name: "child5",
id: 5
},
{
name: "child6",
id: 6
}
]
},
{
name: "parent4",
id: 3,
blog: [
{
name: "child6",
id: 7
}
]
},
]
var result = objects.filter(value => {
if(!value.blog) return;
return value.blog.some(blog => blog.id !== rm)
})
console.log(result);
What is wrong here, or some one show me the correct approach?
looking for :
need to remove the blog if the id is same as rm, parent with other children required to exist.
need to remove the parent, after remove the children, in case there is no child(blog) exist.
Live Demo
Loop through the list of parents, and inside that loop, try to remove blogs with the given id first. Once you have done that, you can check if the blogs property has became empty, and if so, filter it out:
// We're going to filter out objects with no blogs
var result = objects.filter(value => {
// First filter blogs that match the given id
value.blog = value.blog.filter(blog => blog.id !== rm);
// Then, if the new length is different than 0, keep the parent
return value.blog.length;
})
I think the below code is what you are looking for
var result = objects.map(value => {
const blog = value.blog.filter(blog => blog.id !== rm);
if(blog.length === 0) {
return;
}
value.blog = blog;
return value;
}).filter(item => item);
Demo: https://jsfiddle.net/7Lp82z4k/3/
var result = objects.map(parent => {
parent.blog = parent.blog.filter(child => child.id !== rm);
return parent}).filter(parent => parent.blog && parent.blog.length > 0);
I am able to find the object in the javascript below but it is pretty horrible and I am doing the find twice.
I'd be interested in a better way and one that did not involve lodash which I am current not using.
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const selected = statuses.find(status => {
const none = status.items.find(alert => {
return alert.name === 'none';
});
return !!none;
});
console.log(selected)
const a = selected.items.find(s => s.name === 'none');
console.log(a)
You could use a nested some and find like this. This way you can skip the the operation once a match is found.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
let found;
statuses.some(a => {
const s = a.items.find(i => i.name === "none");
if (s) {
found = s;
return true
} else {
return false
}
})
console.log(found)
You could do something like this using map, concat and find. This is a bit slower but looks neater.
const statuses=[{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
const found = [].concat(...statuses.map(a => a.items))
.find(a => a.name === "none")
console.log(found)
Here's a jsperf comparing it with your code. The first one is the fastest.
You could combine all the status items into one array with reduce() and then you only have to find() once:
const statuses = [{
items: [{
id: 1,
name: 'foo'
}, {
id: 5,
name: 'bar'
}]
}, {
items: [{
id: 1,
name: 'mook'
}, {
id: 2,
name: 'none'
}, {
id: 3,
name: 'soot'
}]
}]
const theNones = statuses
.reduce(function(s, t) { return s.items.concat(t.items); })
.find(function(i) { return i.name === 'none'; });
console.log(theNones);
You could use flatMap and find:
Note: Array.prototype.flatMap is in Stage 3 and not part of the language yet but ships in most environments today (including Babel).
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr
.flatMap(({ items }) => items)
.find(({
name
}) => name === 'none')
)
We could polyfill the flatting with concating (via map + reduce):
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}];
console.log(
arr
.map(({ items }) => items)
.reduce((items1, items2) => items1.concat(items2))
.find(({
name
}) => name === 'none')
)
You could also reduce the result:
var arr = [{items:[{id:1,name:'foo'},{id:5,name:'bar'}]},{items:[{id:1,name:'mook'},{id:2,name:'none'},{id:3,name:'soot'}]}]
console.log(
arr.reduce((result, {
items
}) =>
result ? result : items.find(({
name
}) => name === 'none'), null)
)
One reduce function (one iteration over everything), that returns an array of any object matching the criteria:
const [firstOne, ...others] = statuses.reduce((found, group) => found.concat(group.items.filter(item => item.name === 'none')), [])
I used destructuring to mimic your find idea, as you seem chiefly interested in the first one you come across. Because this iterates only once over each item, it is better than the alternative answers in terms of performance, but if you are really concerned for performance, then a for loop is your best bet, as the return will short-circuit the function and give you your value:
const findFirstMatchByName = (name) => {
for (let group of statuses) {
for (let item of group.items) {
if (item.name === name) {
return item
}
}
}
}
findFirstMatchByName('none')