use ES6 .map() on multiple times nested objects for react components - javascript

How can I iterate through this object using .map():
state = {
contacts: [
{ "id":1,
"name":"Leanne Graham",
"email":"Sincere#april.biz",
"address":{
"street":"Kulas Light",
"city":"Gwenborough",
"geo":{
"lat":"-37.3159",
"lng":"81.1496"
}
},
"phone":"1-770-736-8031",
},
{ "id":2,
"name":"Ervin Howell",
"email":"Shanna#melissa.tv",
"address":{
"street":"Victor Plains",
"city":"Wisokyburgh",
"geo":{
"lat":"-43.9509",
"lng":"-34.4618"
}
},
"phone":"010-692-6593",
}
]
}
so map over the contacts will work because is an array and all data like id, name, email and phone is accessible but if I want to iterate over the address, is crashing. I have used some example like:
render(){
const {contacts} = this.state
return(
<>
{Object.keys(contacts.address).map((address, index) => (
<span className="d-block" key={index}>{contacts.address[address]}</span>
))}
</>
);
}
which should work with address but is crashin on geo{} and at this point I have lost the signal.
Anyone can give me an ideea ?

This should help:
const address = contacts[0].address;
<>
{Object.keys().map((addressKey, index) => (
<span className="d-block" key={index}>
{typeof address[addressKey] === "object"
? Object.keys(address[addressKey]).map(e => (
<span>{address[addressKey][e]}</span>
))
: contacts[0].address[address]}
</span>
))}
</>;

I don't think is a matter as long as it displays them
After you mapped the contacts you can excess the addresses properties as you like:
const contacts = [
{
id: 1,
name: "Leanne Graham",
email: "Sincere#april.biz",
address: {
street: "Kulas Light",
city: "Gwenborough",
geo: {
lat: "-37.3159",
lng: "81.1496",
},
},
phone: "1-770-736-8031",
},
{
id: 2,
name: "Ervin Howell",
email: "Shanna#melissa.tv",
address: {
street: "Victor Plains",
city: "Wisokyburgh",
geo: {
lat: "-43.9509",
lng: "-34.4618",
},
},
phone: "010-692-6593",
},
];
const addresses = contacts.map(({ address, id }) => ({
id,
...address,
}));
console.log(addresses);
Like rendering them:
addresses.map(({ street, city, id }) => (
<span className="d-block" key={id}>
{street}:{city}
</span>
))

You can map over an array because you expect it to have consistent values through each element, but that is not really the case for object. All the values are different and have different meaning. Also, your span will not be able to display objects, it will only primitive values such as strings or numbers
You can do what you want to achieve manually.
const { contacts } = this.state;
return (
<>
{contacts.map(({ address }, id) => {
return (
<React.Fragment key={id}>
<span>Street: {address.street}</span>
<span>City: {address.city}</span>
<span>Lat: {address.geo.lat}</span>
<span>Lng: {address.geo.lng}</span>
</React.Fragment>
);
})}
If you really want to do it using a loop or some form of iteration you can look into Object.entries. But it would be really difficult to do that with nested objects if you do not know what you are dealing with.
contacts.map(({ address }) => {
for (let [key, value] of Object.entries(address)) {
console.log(`${key}: ${value}`); // logs "street: Kulas Light"
}
})
but note for geo it will give "geo: [Object object]" if you put this directly into a span.
P.S I would recommend finding a better key than the index of array for the Fragment.

Related

Loops inside react components leading to rendering of sub components

I have a data as below
myArr = [
{
"Sl.No": "n1",
company: "ABC",
Name: "Sam",
Designation: "Architect",
Salary: "100",
},
{
"Sl.No": "n2",
company: "ABC",
Name: "Bill",
Designation: "Engineer",
Salary: "200",
},
{
"Sl.No": "n3",
company: "ABC",
Name: "Jill",
Designation: "HR",
Salary: "300",
},
{
"Sl.No": "n4",
company: "XYZ",
Name: "Bill",
Designation: "Engineer",
Salary: "250",
},
{
"Sl.No": "n5",
company: "XYZ",
Name: "Tom",
Designation: "Mechanic",
Salary: "150",
},
{
"Sl.No": "n6",
company: "LMN",
Name: "Tom",
Designation: "Mechanic",
Salary: "150",
},
];
I want to create a react app which shows the data as below. Nothing but listing the employees & their designations under the name of the company.
The boxes on the right are number of doses of vaccine taken (data comes from somewhere else)
I have the components set-up like so
Inside App.js
I have left out importing the components , css & all that for simplcity
export const App = () => {
return (
<div className=app}>
<CompanyInfo />
</div>
);
}
Inside CompanyInfo.js
export const CompanyInfo= () => {
let companies= [...new Set(myArr.map((item) => item.company))];
const renderingComponents = (company: string, index: number) => {
return (
<Fragment key={index}>
<p className="company-name">{company}</p>
<div className="category-employees">
<CompanyEmployee toggled={toggled} />
</div>
;
</Fragment>
);
};
return (
<div className=company-info}>
{companies.map((item, index) => renderingComponents(item, index))}
</div>
);
}
So far, so good, I can render the titles of all the companies & I have hardcoded the CompanyEmployee to see if it populates within every company & it does. However, I want CompanyEmployee to be dynamic & I am not able to figure our how to pass the company related info to the components (that data will have info of all the company employees) and then map the CompanyEmployee component on that data.
Inside CompanyEmployee.js
Please note that this is like a wrapper for 2 components
export const CompanyEmployee= () => {
return (
<div className=ce}>
<EmployeePrimaryDetails />
<EmployeeVacDetails />
</div>
);
}
Inside EmployeePrimaryDetails.js
export const EmployeePrimaryDetails= (props) => {
return (
<div className=epd>
<span className="name">{props.Name}</span>
<span className="designation">{props.Designation}</span>
</div>
);
}
Can anyone guide me on how I render EmployeePrimaryDetails.js for each employee of the company?
I tried to do a for of, forEach, map in the renderingComponents function of CompanyInfo itself but when I try that I get the Typescript error "Expression Expected" (I am using typescript with React in my project).
Any help is appreciated.
Inside renderingComponents function you can say:
const filteredList = myArr.filter(employee => employee.company === company);
filteredList.map(employee => (<CompanyEmployee employee={employee} toggled={toggled} />));
Resulting in this:
const renderingComponents = (company: string, index: number) => {
const filteredList = myArr.filter(employee => employee.company === company);
return (
<Fragment key={index}>
<p className="company-name">{company}</p>
<div className="category-employees">
{filteredList.map(employee => (<CompanyEmployee employee={employee} toggled={toggled} />))}
</div>
;
</Fragment>
);
};
So in CompanyEmployee component you can destructure the data you need.

how to map an array of objects that has values of array of objects

I have a list of comments that I fetch from my API looking like this. A comment has likes and dislikes alongside the user who posted the like or dislike.
const comments = [
{
id: 1,
comment: "nice",
author: "jenny",
likes: [
{
id: 1,
user: "alice"
},
{
id: 2,
user: "bob"
}
],
dislikes: [
{
id: 3,
user: "sam"
}
]
},
{
id: 2,
comment: "good job",
author: "alice",
likes: [
{
id: 2,
user: "bob"
}
],
dislikes: [
{
id: 3,
user: "sam"
}
]
}
]
Lets say Alice is logged in.
Now the map to display the comments works fine, but I want to do "nested map" which loops through the likes and dislikes array of objects within the same object array and see if the user of a like or dislike matches who is logged in to display a confirmation.
My current code:
const signedInUser = "alice";
return(
Object.keys(comments).map((x, index) => {
const com = comments[x];
<div className="comment" key={index}>
<p>{com.author}</p>
<p>{com.comment}</p>
{com.likes.map((x) => {
{
x.user === signedInUser ? (
<p>You liked this</p>
) : (
<button>Like</button>
);
}
})}
</div>;
})
);
I am doing this with react, since I am trying to
You are suppose to use some to get a boolean response if there exist a like by the same user.
map return response for every object in the like array.
return(
Object.keys(comments).map((x, index) => {
const com = comments[x];
<div className="comment" key={index}>
<p>{com.author}</p>
<p>{com.comment}</p>
{com.likes.some((x) => x.user === signedInUser)?
(
<p>You liked this</p>
) : (
<button>Like</button>
);
}
</div>;
})
);
const renderLikeOrDislikeButton = (arr, text1, ) =>{
if(arr.some((x) => x.user === signedInUser)){
return <p>You {text1} this</p>
}
return <button>{text2}</button>
}
return(
{
Object.keys(comments).map((x, index) => {
const com = comments[x];
return(
<div className="comment" key={index}>
<p>{com.author}</p>
<p>{com.comment}</p>
{renderLikeOrDislikeButton(com.likes, "liked", "Like")}
{renderLikeOrDislikeButton(com.likes, "disliked", "Dislike)}
</div>
)
});

Compare Two values in Javascript then map

I have Different data from different Schema. I want to compare data from both schema to find out if they are same then map through them.
allStaff = [
{
role: "admin",
name: "jamal",
branch: {
name: "kansas",
},
},
{
role: "legal",
name: "keith",
branch: {
name: "arizona",
},
},
{
role: "admin",
name: "dwyane",
branch: {
name: "kansas",
},
},
...
];
contributor = {
fullName: 'Aaraf',
branch: {
name: "kansas",
},
};
I want the form option for this contributor to contain only staff in same branch as he is(kansas).
<Form.Group controlId="branch">
<Form.Label>Investment Officer:</Form.Label>
<Form.Control
as="select"
value={investmentOfficer}
onChange={(e) => setInvestmentOfficer(e.target.value)}
>
<option>Investment Officer</option>
{(allStaff.branch?.name === contributor.branch?.name).map(
staff) => (
<option key={staff._id} value={staff._id}>
{staff.name}
</option>
)
)}
</Form.Control>
</Form.Group>
You want to filter your data first before you map it so that allStaff is filtered down where the branch names match. I have provided the code to do so below.
allStaff.filter((staff) => staff.branch.name === contributor.branch.name)
.map((filteredStaff) => (
<option key={staff._id} value={staff._id}>
{staff.name}
</option>
));
the problem is your code trying to map a boolean... .map() is built in attribute for array, also your code not explaining what is you trying to iterate, so the first step figure out which array you want to iterate first, after that follow this example,
for this example, im going to assume that you want to iterate allStaff.branch
{allStaff.branch.map(
(branch,i) => {
if(branch?.name === contributor.branch[i].name)
return (<option key={branch._id} value={branch._id}>
{branch.firstName}
</option>)
})}

Dynamic rendering React components using data from multiple JSON objects and arrays

I am using React Native to dynamically render components. Currently the data is being taken by mapping over an array of objects which looks similar to:
const OBJECTS = [
{name: "Object 1", logo: require('../logos/object1.png')},
{name: "Object 2", logo: require('../logos/object2.png')},
{name: "Object 3", logo: require('../logos/object2.png')},
]
I then map over these objects to render a card for each object like so:
<View>
{object.map(item => {
return (
<View key={item.key}>
<Card>
<Text>{item.name}</Text>
<Image source={item.logo}/>
</Card>
</View>
);
})}
</View>
However now I would like to add additional items to each card which are taking information fetched from an API and retrieved in JSON form. These JSON objects look similar to the following:
Request 1:
fetchPriceActionCreator {
"items": [{
"objectId": "object1",
"price": 10
},
{
"objectId": "object2",
"price": 5
},
{
"objectId": "object3",
"price": 20
}
]
}
Request 2:
fetchBalanceActionCreator {
"items": [{
"objectId": "object1",
"balance": 2,
"account": 30022
},
{
"objectId": "object2",
"balance": 4,
"account": 40035
},
{
"objectId": "object3",
"balance": 6,
"account": 50021
}
]
}
As the application is growing in complexity, you may notice from the action creators used to make the requests that I have begun to use Redux. My challenge is now figuring out a way to associate the retrieved JSON information, with the initial OBJECTS object array so that I can include this information in the relevant dynamically rendered card displaying the name, logo, balance, and price of each object.
From what I understand, I need to take the static information from the OBJECTS object array, and move this to initialState in either the reducer or store - however I am completely lost as to how to go about doing this, and how to then associate the two retrieved JSON object arrays, with the OBJECT object array.
The end result should look something like this:
<View>
{object.map(item => {
return (
<View key={item.key}>
<Card>
<Text>{item.name}</Text>
<Image source={item.logo}/>
<Text>{item.price}</Text>
<Text>{item.balance}</Text>
</Card>
</View>
);
})}
</View>
Any help would be greatly appreciated.
EDIT:
Currently my redux reducers look like so:
function priceReducer(priceState = {}, action) {
switch (action.type) {
case REQUEST_PRICE:
return Object.assign({}, priceState, {
isFetching: true,
didInvalidate: false,
});
case RECEIVE_PRICE:
return Object.assign({}, priceState, {
isFetching: false,
didInvalidate: false,
lastUpdated: action.lastUpdated,
items: action.items,
});
default:
return Object.assign({}, priceState);
}
}
in your redux store you can update the state by through merging the initial state and the data coming from the api, like so
const priceState = {
// some other props
OBJECTS: [
{ name: "Object 1", logo: require('../logos/object1.png') },
{ name: "Object 2", logo: require('../logos/object2.png') },
{ name: "Object 3", logo: require('../logos/object2.png') },
] // your initial Data as you posted.
}
function priceReducer(priceState = {}, action) {
switch (action.type)
{
case REQUEST_PRICE: {
return {
...priceState,
didInvalidate: false,
isFetching: true,
OBJECTS: priceState.OBJECTS.map((obj) => {
const i = action.items.find((item) => item.objectId == obj.name) || {}; // using the conditional operator to avoid runtime error if no item was found for the given object
// Example: current object name is "object1", i will the item inside the payload that will have the its objectId equal to "object1"
return {
...obj,
...i /// i will be the item inside the payload that has the same object id as the current object name
}
})
}
}
case RECEIVE_PRICE: {
return {
...priceState,
didInvalidate: false,
lastUpdated: action.lastUpdated,
OBJECTS: priceState.OBJECTS.map((obj) => {
const i = action.items.find((item) => item.objectId == obj.name) || {}; // using the conditional operator to avoid runtime error if no item was found for the given object
// Example: current object name is "object1", i will the item inside the payload that will have the its objectId equal to "object1"
return {
...obj,
...i /// i will be the item inside the payload that has the same object id as the current object name
}
})
}
}
default: return priceState;
}
}
if you are not sure what the ... is its called the spread operator., which is equivalent to Object.assign that you were using

JavaScript shortest way to map an array of objects to a new array of new objects?

I have an array of person objects, where each person have an array of profiles objects which consist of name and an image url, and a list of addresses objects which consist of lat and long properties, as follows:
var listOfPersons = [{
addresses : [{lat:11, long:11}, {lat:22, long:22}],
profile: [{image:"some_url1", name: "peter parker"}]
},
{
addresses : [{lat:33, long:33}, {lat:44, long:44}],
profile: [{image:"some_url2", name: "bruce wayne"}]
}];
I need to create a new array of objects, where each new object has an image, long, lat properties, for each set of lat long, as follows:
var expectedResult = [
{
image:"some_url1",
lat:11,
long:11
},
{
image:"some_url1",
lat:22,
long:22
},
{
image:"some_url1",
lat:33,
long:33
},
{
image:"some_url1",
lat:44,
long:44
}
];
What is the shortest way (in terms of writing code) to map\ reduce the first array into the second?
You can use nested Array.flatMap() with Array.map() to iterate the array/address/profiles, and combine image and lat, long properties into a single object:
const listOfPersons = [{"addresses":[{"lat":11,"long":11},{"lat":22,"long":22}],"profile":[{"image":"some_url1","name":"peter parker"}]},{"addresses":[{"lat":33,"long":33},{"lat":44,"long":44}],"profile":[{"image":"some_url2","name":"bruce wayne"}]}];
const result = listOfPersons.flatMap(o =>
o.addresses.flatMap(({ lat, long }) =>
o.profile.map(({ image }) => ({
image,
lat,
long
}))
)
);
console.log(result);
If you always use just the 1st profile always, you can remove one level of Array.flatMap():
const listOfPersons = [{"addresses":[{"lat":11,"long":11},{"lat":22,"long":22}],"profile":[{"image":"some_url1","name":"peter parker"}]},{"addresses":[{"lat":33,"long":33},{"lat":44,"long":44}],"profile":[{"image":"some_url2","name":"bruce wayne"}]}];
const result = listOfPersons.flatMap(o =>
o.addresses.map(({ lat, long }) => ({
image: o.profile[0].image,
lat,
long
}))
);
console.log(result);
You can use Array.prototype.reduce() with combined Array.prototype.forEach().
The documentation states for reduce():
The reduce() method executes a reducer function (that you provide) on each element of the array, resulting in a single output value.
I think the following can work for you:
const listOfPersons = [{
addresses : [{lat:11, long:11}, {lat:22, long:22}],
profile: [{image:"some_url1", name: "peter parker"}]
},
{
addresses : [{lat:33, long:33}, {lat:44, long:44}],
profile: [{image:"some_url2", name: "bruce wayne"}]
}];
const result = listOfPersons.reduce((acc, cur) => {
cur.addresses.forEach(e => acc.push({ ...e, image: cur.profile[0].image }));
return acc;
}, []);
console.log(result);
I hope that helps!
Sinced you asked for shortest in writing code:
var listOfPersons = [{addresses: [{lat:11, long:11}, {lat:22, long:22}],profile: [{image:"some_url1", name: "peter parker"}]},{addresses:lat:33, long:33}, {lat:44, long:44}],profile: [{image:"some_url2", name: "bruce wayne"}]}];
const res = listOfPersons.reduce((r,{addresses:a,profile:[{image}]})=> [...r,...a.map(o=>({image,...o}))],[]);
console.log(res);
Here's with formatting and better variable names:
var listOfPersons = [{
addresses : [{lat:11, long:11}, {lat:22, long:22}],
profile: [{image:"some_url1", name: "peter parker"}]
},
{
addresses : [{lat:33, long:33}, {lat:44, long:44}],
profile: [{image:"some_url2", name: "bruce wayne"}]
}];
const res = listOfPersons.reduce((acc, { addresses: adr, profile: [{image}] }) =>
[...acc, ...adr.map(a => ({image, ...a}) )],
[]);
console.log(res);
var listOfPersons = [
{
addresses: [{ lat: 11, long: 11 }, { lat: 22, long: 22 }],
profile: [{ image: "some_url1", name: "peter parker" }]
},
{
addresses: [{ lat: 33, long: 33 }, { lat: 44, long: 44 }],
profile: [{ image: "some_url2", name: "bruce wayne" }]
}
];
var expectedResult = listOfPersons.reduce(
(acc, person) => ([
...acc,
...person.addresses.map(
address => ({ ...address, image: person.profile[0].image })
)
]),
[]
)
This will give you what you want assuming you always want the first result from .profile
var listOfPersons = [{
addresses : [{lat:11, long:11}, {lat:22, long:22}],
profile: [{image:"some_url1", name: "peter parker"}]
},
{
addresses : [{lat:33, long:33}, {lat:44, long:44}],
profile: [{image:"some_url2", name: "bruce wayne"}]
}];
var expectedResult = [];
expectedResult.forEach(person => {
person.addresses.forEach(address => expectedResult.push({image: person.profile[0].image, ...address}))
});

Categories

Resources