Using Map function inside of another Map function in React Js - javascript

I want to add genre to my Movie Search app's carousel using map function. I have two json files one of them is an array of movie data as objects having a key as genre_ids eg:
genre_ids: 0: 16 1: 28 2: 12 3: 35 4: 10751 5: 14
and other contains an array of objects specifying the name of the genre with a unique key. eg:
{ "genres": [ { "id": 10759, "name": "Action & Adventure" } ] }
The Json files:
Movie data: https://api.themoviedb.org/3/trending/all/day?api_key=921345714956c7d9c3db36ac3f20ee09
Genre: https://api.themoviedb.org/3/genre/movie/list?api_key=921345714956c7d9c3db36ac3f20ee09&language=en-US
I tried mapping the json with movie data first. Then while mapping the genre i checked if the id of the genre matches with that of the id of the movie data element. Then I returned the name of that particular genre. But it is returning the names of all the genre present in the genre json.
My code:
{
trend.map((e, i) => {
return (
<div className="carousel-item" key={i}>
<img
src={`${img}${e.backdrop_path}`}
className="d-block carimage" alt="..." />
<div className="carousel-caption d-none d-md-block">
<div className='caro-discription'>
<button className='trailer-main-button'>More Info!!</button>
<h1>{e.title?e.title:e.name}</h1>
<h3>Rating: {e.vote_average}/10</h3>
</div>
<div className='genre-list'>
{genre.map((a,b)=>{
return(
<h3 className='genre-list-item' key={b}>{a.id=e.genre_ids? a.name: null}</h3>
)
})}
</div>
</div>
</div>
)
})
}

Change
<h3 className='genre-list-item' key={b}>{a.id=e.genre_ids? a.name: null}</h3>
to
<h3 className='genre-list-item' key={b}>{a.id===e.genre_ids? a.name: null}</h3>

There are a couple of things you're doing wrong here. First is the use of map instead of find. Your Movie object has one genre_ids property which is always a positive integer. This means what you don't need to map all genres which match the genre_ids, instead you just need to find the specific genre your movie is tagged as:
<h3 key={e.genre_ids * e.id}>{genres.find((g, idx) => g.id === e.genre_ids).name}</h3>
If you do have multiple genres, then you'd use map:
{genres.map((g, idx) => e.genre_ids.includes(g.id) ? <h3 key={e.id * g.id}>{g.name}</h3> : null )}
This way you save processing time.
Also, your code has the issue of using = for comparison. A single equals sign is used for assignment. If you want to check equality between two values, you'd use ==. If you want to check if the two are of the same type as well, you'd use ===.

Related

Comparing Arrays using .map (ReactJS)

In my project, I have 2 arrays being read from Firestore.
courseList - A list of courses of which a user is enrolled in
courses - A list of all of the courses available in the project
I would like to compare these using a .map so that in my course portal, only the courses of which the user is enrolled in is rendered.
Here is what the arrays look like:
courseList:
courses
I know the arrays work, however, the .map doesn't seem to be working!
Here's my code:
const {courses} = this.state
const {courseList} = this.state
{
courses.length && courses.map (course => {
if (course.courseUrl === courseList.CourseCode) {
return (
<div className = "CourseTile" key = {course.courseName}>
<div className = "CourseTitle">
<h1> {course.courseName}</h1>
</div>
<div className = "CourseDescription">
<p> {course.courseSummary}</p>
</div>
<Link to={`/course/${course.courseUrl}/courseinformation`}> <button className = "LetsGoButton"> Take course</button> </Link>
</div>
)
}
else return null;
}
)
}
If I replace
if (course.courseUrl === courseList.CourseCode)
with
if (course.courseUrl === "websitedesign")
It renders the website design course only, So I believe there's something wrong with this line.
Any help would be appreciated.
You are correct in where the problem lies:
course.courseUrl === courseList.CourseCode
In this case course is a single item from a list, with a property courseUrl. That's fine. But courseList is an array of items, each of which has a CourseCode property. The Array itself does not (although, interestingly, it could).
It seems like what you are trying to do is pull the full course data (from courses) but filtered to only the ones the user has. In this case, you have to loop through one list, looking through the other list for each item. What you want is filter (or, more powerfully, reduce) but probably not map.
const filteredCourses = availableCourses.filter( availableCourse => studentsCourses.some( studentsCourse => studentsCourse.id === availableCourse.id ) );
You'll notice I renamed the variables to make it clear which of the two lists is being used at each part.
The outer function filter will return a new array containing only those items that return 'true' in the callback function.
The inner callback function some loops through another array (the student's enrolled courses) and returns true if it finds any that match the given condition.
So in English, "Filter this list of all the courses, giving me back only the courses that have a matching ID in the list of the student's enrolled courses."
if (course.courseUrl === courseList.CourseCode)
You can filter the courses array by checking if each course is included in the courseList array, matching an URL to a courseList element's CourseCode property. array.prototype.some is used to iterate the course list and check that at least one courseList item matches. Once filtered you can map the filtered result as per normal.
const {courses} = this.state;
const {courseList} = this.state;
...
{courses
.filter(({ courseUrl }) =>
courseList.some(({ CourseCode }) => CourseCode === courseUrl)
)
.map((course) => {
return (
<div className="CourseTile" key={course.courseName}>
<div className="CourseTitle">
<h1> {course.courseName}</h1>
</div>
<div className="CourseDescription">
<p> {course.courseSummary}</p>
</div>
<Link to={`/course/${course.courseUrl}/courseinformation`}>
{" "}
<button className="LetsGoButton"> Take course</button>{" "}
</Link>
</div>
);
})}

ReactJS - How to remove an item from an array in a functional component?

I am trying to create a simple shopping cart using ReactJS and I figured a potential way out but whenever I click on the remove button I've set it doesn't really remove the items from the cart..
So those are my state managers right here:
let[product, setProduct] = useState([])
//The function bellow is what I use to render the products to the user
const[item] = useState([{
name: 'Burger',
image: '/static/media/Burger.bcd6f0a3.png',
id: 0,
price: 16.00
},
{
name: 'Pizza',
image: '/static/media/Pizza.07b5b3c1.png',
id: 1,
price: 20.00
}])
and I have a function that adds the objects in item to the product array, then I have a function that is supposed to remove them that looks like this:
const removeItem=(idx)=>
{
// let newProduct = product.splice(idx,1)
// setProduct([product,newProduct])
// $('.showItems').text(product.length)
// product[idx]=[]
product.splice(idx,1)
if(product.length<=0)
{
$('.yourCart').hide()
}
}
This function is called from here:
{product.map((item, idx)=>
<div className='yourCart' key={idx}>
<hr></hr>
<div>
<img src ={item.image}></img>
<h3 className='burgerTitle'>{item.name}</h3>
<h4><strong>$ {item.price}.00</strong></h4>
<Button variant='danger' onClick={()=>removeItem(idx)}>Remove</Button>
</div>
<br></br>
</div>)}
The problem is that I've tried to use splice, setState, I tried to even clear the entire array and add the elements that are left after applying the filter function to it but it was all to no avail.
How can I make it so that when I click on the remove button it removes the specific item from the array??
You need to use the mutation method setProduct provided from the useState hook to mutate product state.
const removeItem = (id) => {
const index = product.findIndex(prod => prod.id === id); //use id instead of index
if (index > -1) { //make sure you found it
setProduct(prevState => prevState.splice(index, 1));
}
}
usage
<Button variant='danger' onClick={()=>removeItem(item.id)}>Remove</Button>
as a side note:
Consider using definite id values when working with items in an array, instead of index in array. the index of items can change. Use the item.id for a key instead of the index when mapping. Consider using guids as identification.
{product.map((item, idx)=>
<div className='yourCart' key={`cartItem_${item.id}`}> //<-- here
<hr></hr>
<div>
<img src ={item.image}></img>
<h3 className='burgerTitle'>{item.name}</h3>
<h4><strong>$ {item.price}.00</strong></h4>
<Button variant='danger' onClick={()=>removeItem(item.id)}>Remove</Button>
</div>
<br></br>
</div>)}
You can define removeItem as a function, which gets an id (instead of an index, since that's safer) and setProduct to the subset which should remain. This could be achieved in many ways, in this specific example I use .filter() to find the subset of product whose elements differ in their id from the one that is to be removed and set the result as the new value of product.
removeItem = (id) => {
setProduct(product.filter((i)=>(i.id !== id)))
}

Grouping over an {#each} loop in Svelte

I have an array of non-homogeneous objects that are each rendered in a loop using a <svelte:component this={type}> component, and I would like to group adjacent things that are of the same type within a div.
For example, I have some code similar to this:
<script>
let things = [
{type: A, content: "One"},
{type: B, content: "Two"},
{type: B, content: "Three"},
{type: A, content: "Four"}
];
</script>
{#each things as thing, i}
{()=>someMagicHere1(things, thing, i)}
<svelte:component this={thing.type}>
{()=>someMagicHere2(things, thing, i)}
{/each}
And I want the output to group the things like so:
<div class="group type-A">
<div class="thing type-A">One</div>
</div>
<div class="group type-B">
<div class="thing type-B">Two</div>
<div class="thing type-B">Three</div>
</div>
<div class="group type-A">
<div class="thing type-A">Four</div>
</div>
In the things array, the things are not sorted (actually, they are, but by date, unrelated to their type), but the idea is to visually group together the ones that are the same type. Ideally, I'd be able to group only certain types (like group all of type A, but type B's would remain separate), but I feel like I would be able to derive a separate solution if I could group at all. There are also more than two types; this is just a minimal sample.
In Svelte, the individual A and B components can't have partial HTML elements like this inside, because Svelte won't allow conditionals around unclosed elements:
{#if groupStart}
<div class="group">
{/if}
From within each someMagicHereX() I could output some HTML with {#html customTags} to get the DOM output that I want, but then I lose the style encapsulation and other Svelte component benefits.
What I'd really like is a more "sveltian" solution. Perhaps I need to create something new with use? Anyone have any good ideas?
Update: A key feature I seem to have left out is that any controls must ultimately bind to the original dataset. So even if the data is transformed somehow, the original data must be updated at runtime and vice-versa on any bound controls.
Not absolutely sure if the following satisfies what you are looking for, as I can't tell from your question if you want to keep the order of values untouched or if you want to reorder them by group first.
The idea is to re-arrange your data in order to loop through it in the way you want to. It's always easier to manipulate data in order to fit a layout than the other way around.
The following would turn your initial array into an array with the following structure:
[
{
cssClass: 'type-A',
values: [ /* all objects of type 'A' */ ],
},
{
cssClass: 'type-B',
values: [ /* all objects of type 'B' */ ],
},
// etc.
]
The data transformation is pretty straightforward:
let groups = things.reduce((curr, val) => {
let group = curr.find(g => g.cssClass === `type-${val.type}`)
if (group)
group.values.push(val)
} else {
curr.push({ cssClass: `type-${val.type}`, values: [ val ] })
}
return curr
}, [])
With this new data structure available, it's fairly easy to achieve the layout you had in mind:
{#each groups as group}
<div class="group {group.cssClass}">
{#each group.values as value}
<div class="thing {group.cssClass}">
{value.content}
</div>
{/each}
</div>
{/each}
Demo REPL
Edit: If you prioritize the order of objects as it stands in your initial array, the data transformation would be slightly different. Basically, you'd want to create a new 'group' every time the type changes.
Something like the following would do the trick (note that the svelte #each structure would remain the same, only the data transformation changes):
let groups = things.reduce((curr, val) => {
let group = curr.length ? curr[curr.length - 1] : undefined
if (group && group.cssClass === `type-${val.type}`) {
group.values.push(val)
} else {
curr.push({ cssClass: `type-${val.type}`, values: [ val ] })
}
return curr
}, [])
Option 2 Demo REPL

How to set/edit variables in Object.keys

I'm trying to use use Object.keys in a map function but i'm getting keys based on map value variable but it's giving me error my data object as you can see i'm trying to get the keys of Display and coin value from map variable and the coin value is in the array at the end but it's giving me error
in short i'm want to get the keys of every object in display child object and display child key will be from my coin name array so i don't have to hardcode it
{this.props.crypto_head_coins.map(coin => {
return (
<div key={coin} className="crypto_head">
<span>{coin} </span>
{console.log(Object.keys(this.props.crypto_head_data.DISPLAY.coin))}
<p>{this.props.crypto_head_data.DISPLAY.BTC.USD.PRICE}</p>
</div>
);
})}
This does not work, because DISPLAY is expected to have a literal key called "coin".
Object.keys(this.props.crypto_head_data.DISPLAY.coin);
This will work, because you are referencing the variable coin as a possible key in DISPLAY.
Object.keys(this.props.crypto_head_data.DISPLAY[coin]);
{
this.props.crypto_head_coins.map(coin => {
return (
<div key={coin} className="crypto_head">
<span>{coin}</span>
{console.log(Object.keys(this.props.crypto_head_data.DISPLAY[coin])}
<p>{this.props.crypto_head_data.DISPLAY.BTC.USD.PRICE}</p>
</div>
);
});
}

Merge two objects by their same value to display information | React, Lodash

If I have two objects:
keyedCustomers
item
Both of those objects contain a value I need because I need to check a condition with the object item and then display stuff from keyedCustomers.
In keyedCustomer I have this: _id: "5954aa0433979b24e825de77".
And in item there's this: customer: "5954aa0433979b24e825de77".
As you can see, the values are exactly the same.
I've heard from my friends that I could use Lodash and do something like: _.keyby(blablabla), but I'm not really sure.
All help I can get is much appreciated.
Update! Need to add some code
So basically here's my condition:
if (item.level == 0) {
return (
<div>
<p>Denna användare har inte accepterat: {need name-value from object keyedCustomer}</p>
</div>
)
}
If the value at item.level is equal to 0 a div need's to pop up. And the object item does not have the key name, therefor I need to find a way to merge them.
If keyedCustomers is an array of objects keyedCustomer, each having a name property, then you can use _.find to look up the customer name based on a key that is provided by the customer property of item:
if (item.level == 0) {
let thisCustomer = _.find(keyedCustomers, { '_id': item.customer});
return (
<div>
<p>Denna användare har inte accepterat: {thisCustomer.name}</p>
</div>
)
}

Categories