Iterating through 2 arrays to render matching data - javascript

In my React project I have 2 API calls that gives me 2 arrays for example
const allGenres = [{id: 1, name: "Action"}, {id: 2, name: "Adventure"}, {id: 3, name: "Animation"}];
const specificFilm = [1, 3];
I want to iterate through these 2 arrays to compare them and display genre name if Ids are the same, so for above example it would display Action and Animation.
This is what I've done but I think my approach is not good for performance.
{allGenres.map((genre) =>
specificFilm.map((id) =>
genre.id === id ? <Chip label={genre.name} /> : ""
)
)}
link to Sandbox

You can use a set and reverse your loop to filter first like so :
import "./styles.css";
export default function App() {
const allGenres = [
{ id: 1, name: "Action" },
{ id: 2, name: "Adventure" },
{ id: 3, name: "Animation" }
];
const specificFilm = new Set([1, 3]);
return (
<div className="App">
{allGenres.filter(({ id }) => specificFilm.has(id)).map(genre => <span>
{genre.name}
</span>)}
</div>
);
}

Related

How to update react JS state functional component

How can I change a specific object in a state for example, I have data like this
const colors= [
{
id: 1,
name: "red",
},
{
id: 1,
name: "green",
},
{
id: 2,
name: "blue",
},
];
const storage = [{
id:2,
name:"64 GB"
},
{
id:2,
name:"128 GB"
]
The state value should be like this [{id:1, name:'red"}, {id:2,name:"128GB"}]
Is there any way to update the state based on the id only? For example: I need to loop on the data and if the state.id === id it will update that specific object.
So in our example, I need to update the state to be [{id:1, name:'green"}, {id:2,name:"128GB"}] if the state.id === 1
Thanks in advance
You could always copy the current value in local var, modify and set again the state. Something like:
const [colors, setColor] = useState([
{
id: 1,
name: "red",
},
{
id: 1,
name: "green",
},
{
id: 2,
name: "blue",
},
]);
...
let colorCopy = Object.assign({}, colors); //<-- make a copy of color
colorCopy = colorCopy.map(x => {
if (x.id === 1) x.name = "black";
return x;
}; // modify name property for id === 1
setColor(colorCopy); //<-- set new state
EDIT
#Nicolas Menettrier and #andy mccullough raised a good point of discussion: "Why call Object.assign if map function already does a copy of colors array?" This is correct so you could also write above code in this way:
let colorCopy = colors.map(x => {
if (x.id === 1) x.name = "black";
return x;
}; // modify name property for id === 1
setColor(colorCopy); //<-- set new state
1 line code less.
You can update your array state of objects with a shallow copy and change the value, same as bellow:
import * as React from "react";
export default function App() {
const [colors, setColors] = React.useState([
{
id: 1,
name: "red"
},
{
id: 1,
name: "green"
},
{
id: 2,
name: "blue"
}
]);
const updateState = (id, name) => {
let copy = [...colors];
let color = copy.filter((x) => x.id == id)[0];
if (!color) return;
let index = copy.indexOf(color);
color.name = name;
copy[index] = color;
setColors(copy);
};
return (
<div className="App">
{colors.map((color, index) => (
<>
<p>
{color.id} : {color.name}
</p>
</>
))}
<button
onClick={() => {
updateState(2, "yellow");
}}
>
update
</button>
</div>
);
}

How to display items in React using array.map, useState, useEffect at random with onClick?

I'm trying to make a name generator component that will display a name with its corresponding meaning from an array when the user clicks a button. I'm using useState to set the initial state as well as to cycle through the array randomly, as well as useEffect (although not sure if its necessary). I would prefer to use the array.map() method for randomizing. Component code below:
import React, { useState, useEffect } from "react";
const GeneratorNew = () => {
const startingName = [{ id: 0, value: "Update Name!" }];
const nameList = [
{ id: 1, name: 'Astghik', meaning: 'little star' },
{ id: 2, name: 'Tagouhi', meaning: 'Queen' },
{ id: 3, name: 'Lusine', meaning: 'Moon' }]
;
const [stateOptions, setStateValues] = useState(startingName);
// startingName.push(...nameList);
useEffect(() => {
setStateValues(nameList);
}, []);
return (
<div>
<h1>{nameList.name}</h1>
<p>{nameList.meaning}</p>
<button>
{stateOptions.map((nameList, index) => (
<option key={nameList.id}>{nameList.value}</option>
))}
</button>
</div>
);
};
export default GeneratorNew;
import React, { useState, useEffect } from "react";
const GeneratorNew = () => {
const startingName = [{ id: 0, value: "Update Name!" }];
const nameList = [
{ id: 1, name: 'Astghik', meaning: 'little star' },
{ id: 2, name: 'Tagouhi', meaning: 'Queen' },
{ id: 3, name: 'Lusine', meaning: 'Moon' }]
;
const [stateOptions, setStateValues] = useState(startingName);
const handleOnClick = () => {
setStateValues(nameList[Math.floor(Math.random() * (nameList.length - 1))])
}
return (
<div>
<h1>{nameList.name}</h1>
<p>{nameList.meaning}</p>
<button onClick={() => handleOnClick()}>
{stateOptions.map((nameList, index) => (
<option key={nameList.id}>{nameList.value}</option>
))}
</button>
</div>
);
};
export default GeneratorNew;
No need to useEffect here and useState only having the index is enough.
Try something like this snippet. Generate the random index on each button click.
const GeneratorNew = () => {
const startingName = [{ id: 0, meaning: "Update Name!" }];
const nameList = [
{ id: 1, name: 'Astghik', meaning: 'little star' },
{ id: 2, name: 'Tagouhi', meaning: 'Queen' },
{ id: 3, name: 'Lusine', meaning: 'Moon' }]
;
const [randomIndex, setRandomIndex] = React.useState(-1);
const data = randomIndex !== -1 ? nameList[randomIndex] : startingName[0];
return (
<div>
<h1>{data.name}</h1>
<p>{data.meaning}</p>
<button onClick={() => setRandomIndex(Math.floor(Math.random() * nameList.length))}>
Get Random
</button>
</div>
);
};
ReactDOM.render(<GeneratorNew /> , document.querySelector('#app'))
<script crossorigin src="https://unpkg.com/react#17/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.production.min.js"></script>
<div id="app"> </div>

Display image in table column if image_id equals order_id

I have two arrays of objects. I would like to match order id and image item_id and display an image for order into table column.
I tried forEach over both and setState with image filename, but it only gave me one picture. Is there any other way how to accomplish this. I'm using functional based component in React.
Thanks
// orders array of object
let orders = [
{id: 1, heading: "ddd", description: "ddd", user_created: "Joe"},
{id: 2, heading: "eee", description: "eee", user_created: "Mike"}
]
// images array of object
let images = [
{item_id: 1, filename: "nat-2-large.jpg"},
{item_id: 2, filename: "nat-3-large.jpg"},
]
// comparison
const [filenameForPath, setFilenameForPath] = useState('');
const getFilePath = () => {
ordersArr.forEach(order => {
files.forEach((file) => {
if(order.id == file.item_id){
console.log("success");
setFilenameForPath(file.filename);
}
});
});
}
useEffect(() => {
getFilePath();
},[]);
Here, at the bottom variable mapped is your result.
let orders = [
{id: 1, heading: "ddd", description: "ddd", user_created: "Joe"},
{id: 2, heading: "eee", description: "eee", user_created: "Mike"}
]
// images array of object
let images = [
{item_id: 1, filename: "nat-2-large.jpg"},
{item_id: 2, filename: "nat-3-large.jpg"},
]
const mapped = orders.reduce((arr, item, index)=> {
let res;
images.forEach((file) => {
if(file.item_id === item.id) {
res = {...item, ...file};
}
})
return [...arr, res]
}, [])
console.log(mapped)
If you are sure the number of items will be equal in both and ids will be unique, you can do something like
let result = orders.map((order,index) => order.id === images[index].item_id ? images[index] : null)

How to remove child by id and retrive the parent objects using filter

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);

How to check if objects in array have ID property present in the argument IDs?

I need help how to do this, I have todos and I want to update the todos isComplete property in the array if its ID is in the argument, something like this code:
const todos = [{id: 1, text:"one",isComplete:false},{id:2,text:"two",isComplete:false},{id:3,text:"three",isComplete:false}];
const myArgs = [2,3];
const completeAllTodos = todos.filter(todoItem.id => myArgs.includes(todoItem.id));
completeAllTodos.map( todoItem => todoItem.isComplete = true);
On completeAllTodos I want to return todos objects that have an ID present on the argument array then update completeAllTodos isComplete property to true. I also want to do it asynchronously but I am new in javascript. I've been thinking of how to do it for hours but I can't make my brain do what I want. help?
If you just wanna set isCompletes to true:
const todos = [{id: 1, text:"one",isComplete:false},{id:2,text:"two",isComplete:false},{id:3,text:"three",isComplete:false}];
myArgs = [2,3];
todos.forEach(el=>myArgs.indexOf(el.id)+1?el.isComplete=true:0);
If you want this elems too:
const todos = [{id: 1, text:"one",isComplete:false},{id:2,text:"two",isComplete:false},{id:3,text:"three",isComplete:false}];
myArgs = [2,3];
var result=todos.filter(el=>myArgs.indexOf(el.id)+1).map(el=>!(el.isComplete=true)||el);
And if you want both:
const todos = [{id: 1, text:"one",isComplete:false},{id:2,text:"two",isComplete:false},{id:3,text:"three",isComplete:false}];
myArgs = [2,3];
var results=todos.reduce((arr,el)=>myArgs.indexOf(el.id)+1?(el.isComplete=true,arr.push(el),arr):arr);
http://jsbin.com/wopexiwime/edit?console
If you really need an async implementation (i dont think so):
function forEach(arr,call,i=0){
if(i>=arr.length){
return;
}
call(arr[i],i);
setTimeout(forEach,0,arr,call,i+1);
}
function filter(arr,call,finalcall,i=0,res=[]){
if(i>=arr.length){
return finalcall(res);
}
if(call(arr[i],i)) res.push(arr[i]);
setTimeout(filter,0,arr,call,finalcall,i+1,res);
}
function map(arr,call,finalcall,i=0,res=[]){
if(i>=arr.length){
return finalcall(res);
}
res.push(call(arr[i],i));
setTimeout(map,0,arr,call,finalcall,i+1,res);
}
map([1,2,3,4],(e,i)=>e+i,console.log);
Here's one way.
const todos = [{
id: 1,
text: "one",
isComplete: false
}, {
id: 2,
text: "two",
isComplete: false
}, {
id: 3,
text: "three",
isComplete: false
}];
const myArgs = [2, 3];
const completeAllTodos = todos
.filter(todo => {
return myArgs.includes(todo.id)
})
.map(todo => {
todo.isComplete = true
return todo
});
console.log(completeAllTodos)
Just do it all in the filter loop block:
const todos = [{
id: 1,
text: "one",
isComplete: false
}, {
id: 2,
text: "two",
isComplete: false
}, {
id: 3,
text: "three",
isComplete: false
}];
const args = [2, 3];
const completedTodos = todos.filter(item => args.includes(item.id) && (item.isComplete=true));
console.log(completedTodos);

Categories