can not map array inside of object - arrays

I am lost here, can not find a way how to map through an array that is inside of object. I have received an object from API that includes an array wit values like this:
{
"shelters": [
{
"id": 1,
"name": "Útulok pre psov - TEZAS"
},
{
"id": 2,
"name": "OZ Tuláčik Brezno"
}
]
}
now I need to iterate through the array that is inside and take action on every item to populate the DOM.
my code is:
render() {
// const { isPending, shelters} = this.props;
if (this.props.isPending === false) {
var listOfShelters = this.props.shelters;
console.log('loaded', typeof(listOfShelters), listOfShelters)
return (
<div>
<select style={this.selector} name="útulky" onChange={this.sendId}>
{ listOfShelters.shelters.map(function(shelter, i) {
<option id={shelter.id}>{shelter.name}</option>
})}
</select>
</div>
)
} else if (this.props.isPending === true) {
console.log('fetching')
return (
<select style={this.selector} name="útulky" onChange={this.sendId}>
<option id='0'> Nahravam Data... </option>
</select>
)}
}
I get typeof always an object even if I do this.props.shelters.shelters which should be direct access to that array. if I do this.props.shelters.shelters.map nothing happens, my list of shelters does not get populated. it stays empty. Where do I make the mistake here?

I have found what the problem was. I had different set of parentheses in the .map function, I was using {} instead of (). Now it works like a dream.

Related

map function not working properly in object React

I have an array that contains as follows. I have successfully mapped the data and put it into the elements. but there are some values I couldn't get correctly. I have added a console.log to figure out the data. data is correct in the array, I want to get the Seats ("A1,B1") in here <h5><small>{seatNos.seats}</small></h5> but nothing is displaying. appreciate any help.
Data array
"data": {
"userBookings": [
{
"transactionId": 6357604,
"totalAmount": 350,
"createdAt": "2021-08-05 02:16:48",
"movieName": "Mortal Kombat",
"theaterName": "XxX Cinema",
"showTime": "17:30",
"refcode": "0016048GIN210805I",
"bookedSeats": [
{
"seatType": "Comfert",
"seats": "A1,B1",
"childTickets": 1,
"totalTickets": 2,
"bookedDate": "2021-08-05"
}
]
}
]
},
code
<div className="col-xl-5 col-lg-5 col-md-7 col-sm-7 col-xs-9 col-6" style={{paddingLeft:25}}>
<h5><b>{bookingsData.movieName}</b></h5>
<h5>{bookingsData.theaterName}</h5>
<h5><small>{bookingsData.showTime}</small></h5>
{bookingsData.bookedSeats.map((seatNos) => {
console.log(seatNos.seats);
<h5><small>{seatNos.seats}</small></h5>
})}
{/* <h5><small>{bookingsData.bookedSeats.seats}</small></h5> */}
</div>
You need to return element in map, and set key for this element:
{bookingsData.bookedSeats.map((seatNos, index) => {
console.log(seatNos.seats);
return <h5 key={index}><small>{seatNos.seats}</small></h5>
})}
Your arrow function inside the .map() doesn't return a value. You need a return before the JSX:
{bookingsData.bookedSeats.map((seatNos) => {
console.log(seatNos.seats);
return <h5><small>{seatNos.seats}</small></h5>
})}
Or to use the implicit return arrow function syntax (Either round brackets, or no brackets: () => () or () => returnedValue)
{bookingsData.bookedSeats.map((seatNos) => <h5>
<small>{seatNos.seats}</small>
</h5>)}
This is because you forgot the return
array.map((item) => {
return (
<p>{item.value}</p>
)
})

Loop over Json object -- React

I have a API for which I would like to loop over to get all the value of its keys. But unfortunately Iam only getting the keys for it.
My code until now:
...
const image_link = JSON.stringify(image);
const parsed_link = JSON.parse(image_link);
console.log('1st link', parsed_link[0].Header) // gives "abc.jpg"
...
...
<div>
{
Object.keys(parsed_link).map((e, i) => {
console.log(parsed_link);
console.log(e); // gives integers like 0,1,2 etc...
console.log(i); // gives integers like 0,1,2 etc...
<img src={e} alt="something" width="300" height="200" />;
return null;
})
}
</div>
...
...
API looks like this:
"Header": [
{
"image": "abc.jpg"
},
{
"image": "xyz.jpg"
},
{
"image": "lmn.jpg"
}
]
Please suggest, where am I goin wrong.
Thanks in advance
If you want to loop through an array, you can use map function.
If you want to loop through an object, you can have to convert the object into an array using Object.keys(YOUR_OBJECT) (if you want to get the key) or Object.values(YOUR_OBJECT) (if you want to get the values) than use map function.
You can directly print the array of data into an array of views. You have to return a view like these:
YOUR_ARRAY.map((item, index) => (
<div>YOUR VIEW</div>
))
// or
YOUR_ARRAY.map((item, index) => {
return(
<div>YOUR VIEW</div>
)
})
Note: you can only return a single parent view inside the return value. If you want to return multiple views inside the return value, you have to wrap them inside a single <div></div> or <React.Fragment></React.Fragment> or <></> parent.
In your code, I saw you wrote parsed_link[0].Header. So I assume that the API returns something like this:
[
{
"Header": [
{
"image": "abc.jpg"
},
{
"image": "xyz.jpg"
},
{
"image": "lmn.jpg"
}
],
...
},
...
]
Here is my answer:
<div>
{
parsed_link[0]['Header'].map((item, index) => (
<img
key={index}
src={item}
alt='something'
width='300'
height='200'
/>
)
}
</div>

Items in array are returned in only one JSX element using .map()

New to react and still learning. I have an assignment to create a filter component with three dropsdowns that takes information from a JSON file. The idea is the results in the second dropdown will be filtered once the first dropdown has a selected value. The JSON format is:
"destinations": [
{
"id": 8375,
"name": "Bordeaux",
"country": "France",
"category": "wine"
}, ETC
"seasonCategories": {
"spring": [
"wine",
"wonder",
"forest",
"adventure",
"food"
], ETC
I've created a function that feeds the data into the dropdown component and filters it, but it's not returning as I expect: it's creating only one JSX <option> element with the values of all array items listed inside. I need it to generate a new JSX element with the current value of every item in the array. If I call {el[index]} on the last map function I get the right value, so I'm lost as to why it's not generating each in their own <option> tag. The function I'm using is:
function funcCategories(src, val) {
return Object.keys(src)
.filter((flag) => {
return flag === val;
})
.map((el) => {
let v = [];
for (let i = 0; i < src[el].length; i++) {
v.push(src[el][i]);
}
return v;
})
.map((el) => {
return <option className="Dropdown__option">{el}</option>;
});
}
My Dropdown component:
import React from 'react';
class Dropdown extends React.Component {
constructor(props) {
super(props);
this.value = '';
}
render() {
return (
<div className="Dropdown__wrapper">
<label className="Dropdown__label">{this.props.label}</label>
<select
className="Dropdown__select"
value={this.props.value}
onChange={this.props.handleSelect}
>
<option className="Dropdown__option">{this.props.label}</option>
{this.props.func}
</select>
</div>
);
}
}
export default Dropdown;
For you visual thinkers, this is what I'm seeing in the window:
Dropdown result of my current code
I figured it out! I was pushing the array object in the .filter() method and not the elements of each. Not the cleanest code but it works:
let keys = Object.keys(src);
let j = [];
for (let i = 0; i < keys.length; i++) {
if (keys[i] === val) {
alert(keys[i] + ' === ' + src[keys[i]].length)
for (let h = 0; h < src[keys[i]].length; h++) {
j.push(src[keys[i]][h]);
}
}
}
return j.map((el) => {
return <option className="Dropdown__option">{el}</option>;
});

Filter table with select options (check if column is number or null) in React

I am new at React and I am stuck.
I have a table with data from database. I want to filter that table with a select dropdown. I have mulitple "select-fields" that works fine, because the value in the dropdown matches the exact value in database. But now I just want to check if the column value is null or a number.
In my select options I just want three options (see left picture):
All (Show all result. It is working)
Missing number(is null in Database. Not working)
Has number(Not working)
So the value in the table column (see right picture) I want to filter is either a number or null.
Here is my code so far:
const [filteredData, setFilteredData] = useState([]);
//Column in table:
{
Header: () => (<div>TableHead</div>),
accessor: "accessorToDatabase",
Cell: (props) => { return <div>{props?.cell?.value}</div> }
}
// The select dropdown and the table
<Col>
<Label>Select Dropbox</Label>
<Input type="select" onChange={handleChange('id', 'description')}>
<option>All</option>
<option value="false">Missing number</option>
<option value="true">Has number</option>
</Input>
</Col>
<Table columns={columns} data={filteredData} HandleRowData={HandleRowData} />
//The filter functions
const handleChange = name => ({ target: { value } }) => {
filter[name] = (value === 'All') ? null : value
if (checkProperties(filter)) {
var filtered = state
}
else {
var filtered = handleFilter(state, filter)
}
setFilteredData(filtered)
}
const handleFilter = (arr: Object[], filters: Object) => {
const filterKeys = Object.keys(filters)
return arr.filter(eachObj => {
return filterKeys.every(eachKey => {
if (!filters[eachKey] || !filters[eachKey].length) {
return true
}
})
})
}
I have tried with something like this, for looping through all values in the column, but without no success:
state.map(x=>((
x.id> 0 ? x.id : null)))
.map is used to transform one array of values/objects to another array with the same length but with transformed values/objects. What you want to use is .filter, which removes elements from an array but maintain the element structure.
To keep the ones that does not have an id:
array.filter(x => !x.id)
To keep the ones that has an id that is a number:
array.filter(x => !isNaN(x.id))
A simple Array.prototype.filter should do the trick:
//removes all entries with an id of `0 || null || undefined`
state.filter(entry=>!!entry.id)
//removes all entries with an id of `null || undefined` but keeps the 0
state.filter(entry=>!!entry.id || entry.id === 0)
I would not recommend using isNan: as it internally tries to parse strings to check if they are numbers you might end up with some unexpected behaviors. It also goes wild with booleans, null and undefined.
See this link for more info:
https://www.w3schools.com/jsref/jsref_isnan.asp
EDIT
Rereading your question it looks like you want
all items
items with an ID of type number
items that are not numbers
Here is how you could implement that
const [items,setItems]= useState(someItems)
const [filter, setFilter]=useState('all')
function handleChange(e){
setFilter(e.target.value)
}
const filteredItems = items.filter(item=>{
if(filter === 'number')
return typeof items === 'number'
if(filter === 'no-number')
return typeof item !== 'number'
return true
})
return (
<React.Fragment>
<select onChange={handleChange}>
<option value='all'>All</option>
<option value='no-number'>Missing number</option>
<option value="number">Has number</option>
</select>
<Table columns={columns} data={filteredData} HandleRowData={HandleRowData} />
</React.Fragment>
)
Change the elements with the specific library you are using and you are good to go
I would recommend not using NaN, as it tries to parse strings. Instead, you can create an array and use the map function, in combination with the filter function.

React multiple input (array of inputs) not working

So I'm attempting to render multiple input fields with React.
Everything looks fine until I remove an item. Always the last item is being "removed". If you want to try my code, write "A" in input field 1, "B" in 2, "C" in 3 and remove "B". You'll notice that you have removed "C" instead.
I have tried both value and defaultValue for input to no avail. I have also tried giving a name to the input. I think I am missing a key point here.
Any recommendations?
var MultiInput = React.createClass({
getInitialState: function() {
value = this.props.value
// force at least one element
if (!value || value == '') {
value = [ null ]
}
return {
value: value
}
},
getDefaultProps: function() {
return {
}
},
add_more: function() {
new_val = this.state.value.concat([])
new_val.push(null)
this.setState({ value: new_val })
},
remove_item: function(e, i) {
new_state = this.state.value.concat([])
new_state.splice(i,1)
this.setState({ value: new_state })
},
render: function() {
me = this
// console.log(this.state.value)
lines = this.state.value.map( function(e, i) {
return (
<div key={i}>
<input value={e} />
<button onClick={me.remove_item} >X</button>
</div>
)
})
return (
<div>
{lines}
<button onClick={this.add_more}>Add More</button>
</div>
)
}
})
There are a few things going on here.
To start, you shouldn't use the array index as the key when rendering in an array:
lines = this.state.value.map( function(e, i) {
return (
<div key={i}>
<input value={e} />
<button onClick={me.remove_item} >X</button>
</div>
)
})
The first time through, ["A", "B", "C"] renders:
<div key={0}>
...
</div>
<div key={1}>
...
</div>
<div key={2}>
...
</div>
Then, the second time, once you've removed "B" and left ["A", "C"], it renders the following:
<div key={0}>
...
</div>
<div key={1}>
...
</div>
So, when you removed item at index 1, the item previous at index 2 moves to index 1. You'll want to use some unique value that doesn't change when the position in the array changes.
Second, you should use the empty string instead of null for initialization, and then you'll see that you can't type anything in your inputs. That's because value ensures that an input's value is always whatever you pass it; you'd have to attach an onChange handler to allow the value to be edited.
Changing to defaultValue allows you to type in the box, but when you type, the string in this.state.value doesn't get updated--you'd still need an onChange handler.
Finally, your button has an onClick of this.remove_item, but your remove_item method seems to take the event and index as parameters. However, React will not pass the current index to remove_item; you would need to create a new function that passes the correct params:
onClick={me.remove_item.bind(null, i)}
That said, you really shouldn't call Function#bind inside render as you'll create new functions every time it runs.
Working Code
#BinaryMuse clearly explains why my code above doesn't work: by removing an item from the array and render is called again, the items change position and apparently React's algorithm picks the "wrong changes" because the key we're providing has changed.
I think the simplest way around this is to not remove the item from the array but rather replace it with undefined. The array would keep growing with this solution but I don't think the number of actions would slow this down too much, especially that generating a unique id that doesn't change might involve storing this ID as well.
Here's the working code: (If you wish to optimize it, please check #BinaryMuse's suggestions in the accepted answer. My MultInput uses a custom Input component that is too large to paste here =) )
var MultiInput = React.createClass({
getInitialState: function() {
value = this.props.value
if (!value || value == '') {
value = [ '' ]
}
return {
value: value
}
},
getDefaultProps: function() {
return {
}
},
add_more: function() {
new_val = this.state.value.concat([])
new_val.push('')
this.setState({ value: new_val })
},
remove_item: function(i,e) {
new_state = this.state.value.concat([])
new_state[i] = undefined
this.setState({ value: new_state })
},
render: function() {
me = this
lines = this.state.value.map( function(e, i) {
if (e == undefined) {
return null
}
return (
<div key={i}>
<input defaultValue={e} />
<button onClick={me.remove_item.bind(null, i)} >X</button>
</div>
)
}).filter( function(e) {
return e != undefined
})
return (
<div>
{lines}
<button onClick={this.add_more}>Add More</button>
</div>
)
}
})

Resources