mapping through array of objects and accessing object values - javascript

I am trying to pick a value from array of object and the values can only be from first object.
Firstly i have a array of objects like this:
items [
[
{id: 1, title: "title1", imgUrl: "https://someimage1"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1", imgUrl: "https://someimage1"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1", imgUrl: "https://someimage1"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
]
]
I am planning to display the title and img url on the page. I am using react and this is what i have tried so far:
items.map(item => {
//here i enter each array of the big array individually
//here i will enter first object of the give array
console.log('ITEM:', item[0])
})
in console.log i get this:
ITEM: {id: 1, title: "title1", imgUrl: "https://someimage1"}
ITEM: {id: 1, title: "title1", imgUrl: "https://someimage1"}
ITEM: {id: 1, title: "title1", imgUrl: "https://someimage1"}
but I need to get each title and imgUrl so in my .map() I do this:
items.map(item => {
//here i enter each array of the big array individually
//here i will enter first object of the give array
console.log('ITEM:', item[0].title)
})
but I get this error:
TypeError: Cannot read property 'title' of undefined
I don't understand why I thought I would be able to use dot notation to access whatever key in the object you would like:
for more context:
<div>
{
items.map((item) => {
return <ul>
<li>
<div>
<p>{item[0].title}</p>
<img src={item[0].url}/>
</div>
</li>
</ul>
})
}
</div>
MY SOLUTION TO THE ABOVE PROBLEM
SO i have spend some time messing around with this issue and this works for me:
items.map((item) => {
return item.map ((x, index) => {
if (index === 0) {
return <ul>
<li>
<div>
<p>{ x.title }</p>
<img src={x.url}/>
</div>
</li>
</ul>
}
})
})
Still not sure why my initial idea item[0].title not work without having nested map() function

Try using quotes around the keys:
items [
[
{"id": 1, "title": "title1", "imgUrl": "https://someimage1"},
{"id": 2, "title": "title2", "imgUrl": "https://someimage2"}
]
]

[Update Based on OP updating Post:
Since map builds a new array, using it when you aren't using the returned array is an anti-pattern; use forEach or for-of instead.
Signs you shouldn't be using map:
A) You're not using the array it
returns, and/or
B) You're not returning a value from the callback.
Further reading on .map() via Mozilla
Furthermore nesting 2 loops in such a way will force twice as many iterations as it will still run length times.
If you need to access a sub element and know its index or key then use that value by:
array[index] or object.key and use a single map() (example in full snippet)
If you do actually need to loop over every element (due to some condition not expressed in your question) instead use:
.find() (single elements)
or .filter(elm => elm === 'someValue') (multiple elements)
or .reduce() (multiple elements) with mutating elements as you go
or even .forEach() (multi elements) with performing additional functions outside of array building (you will need to push to something like let loopArray = [])
Continue Original Answer]
Move the ul tag outside the map statement and change url to imgUrl.
Keep in mind if items is generated via async then you will also need to validate items before using map, something like:
{(items && items.length) && items.map(item=>{
/*code*/
)}
Working Snippet (without async check):
const SomeComponent = () => {
const items = [
[
{id: 1, title: "title1.1", imgUrl: "https://p7.hiclipart.com/preview/917/362/567/halloween-trick-or-treating-double-ninth-festival-illustration-halloween-design-elements.jpg"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1.2", imgUrl: "https://image.shutterstock.com/image-vector/silhouette-cat-dog-on-white-600w-671156590.jpg"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
],
[
{id: 1, title: "title1.3", imgUrl: "https://jngnposwzs-flywheel.netdna-ssl.com/wp-content/uploads/2019/05/Transparent-OrangeWhiteCat-768x1030.png"},
{id: 2, title: "title2", imgUrl: "https://someimage2"}
]
];
return (
<div>
<ul>
{items.map((item,index) => {
return(
<li key={String(index)}>
<div>
<p>{item[0].title}</p>
<img style={{width:'10%'}} src={item.imgUrl} alt={item.title}/>
</div>
</li>
)
})}
</ul>
</div>
)
}
ReactDOM.render(
<SomeComponent />,
document.getElementById("react")
);
<div id='react'></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

Related

How can I modify and extend properties of an object located in useState Array?

I'm a react.js beginner, searching for methods to alter my data structure. For example, I want to push new objects into the children-array or remove them by key.
What is the appropriate way to do that?
const [treeData, setTreeData] = useState([
{
title: "parent 1",
key: "0-0",
icon: <UserAddOutlined />,
children: [
{
title: "parent 1-0",
key: "0-0-0",
icon: <UserAddOutlined />,
children: [
{
title: "leaf",
key: "0-0-0-0",
icon: <UserAddOutlined />,
},
{
title: "leaf",
key: "0-0-0-1",
icon: <UserAddOutlined />,
},
],
},
{
title: "parent 1-1",
key: "0-0-1",
icon: <UserAddOutlined />,
children: [
{
title: "sss",
key: "0-0-1-0",
icon: <UserAddOutlined />,
},
],
},
],
},
]);
So you should not update the state directly. It is not allowed.
Maybe where you are receiving data from, suppose via api and the data is response.payload.data etc.
So in your case use the setTreeData(response.payload.data) method to add stuff in it.
Now if you want to update certain value (remove or update using index etc). Obviously you will have to have index somehow.
So for deleting say you will have some click and against that a handler for it
removeItem(e) {
item_to_remove = e.target..... etc // to get the item's reference for matching
setTreeData(treeData.filter(items => item.<someproperty> != item_to_remove))
// In your case could also be targetting children maybe
// setTreeData(treeData.Children.filter(items => item.<someproperty> != item_to_remove))
}
I would say maybe handle childrens' array inside another useState variable (childrenTreeData maybe). But you will have to look it's feasibility too. Just an idea after seeing your data
JUST for INFO
This is something similar I did for updating prices inside each cards in my project
const getCurrentPrice = useCallback(() => { // <======= maybe you do not need this
const updatedTilesData = tilesData.map((tile: any) => {
return {
...tile, // <======= get everything here and then update the price below for item
currentPrice: calculateDNCurrentPrice(
tile.startingPrice,
tile.dnTimestamp
),
};
});
setTilesData(updatedTilesData);
}, [tilesData]);

How to populate a HTML div from array object?

I was YouTube video on ReactJS where it populate a div container from a array object using .map() . Is there any way to do it using vanilla js or jquery in simple HTML ?
ReactJS code is provided below -
ProductCategories.JSX
import { categories } from "../dataList";
import CategoryItem from "./CategoryItem";
<div className="* * *">
{categories.map((item) => (
<CategoryItem item={item} key={item.id} />
))}
</div>
CategoryItem.JSX
import styled from "styled-components";
<Container>
<Image src={item.Image} />
<Info>
<Title>{item.Title}</Title>
<Button>SHOP NOW</Button>
</Info>
</Container>
dataList.js
export const categories = [
{
id: 1,
Image: "https://i.ibb.co/CnkbTqm/download-ixid-Mnwx.jpg",
Title: "Lorem",
},
{
id: 2,
Image: "https://i.ibb.co/CnkbTqm/download-ixid.jpg",
Title: "Ipsum",
},
{
id: 3,
Image: "https://i.ibb.co/CnkbTqm/download.png",
Title: "lorem",
},
];
One way, using reduce():
Loop through the categories using reduce(), for each object return a string (concatenating the previous value) that contains:
A <h1> created with cat.Title
A <img> created with cat.Image
Set the string as innerHTML of your element
Using <body> as demo
const categories = [{id: 1, Image: "https://i.ibb.co/CnkbTqm/download-ixid-Mnwx.jpg", Title: "Lorem", }, {id: 2, Image: "https://i.ibb.co/CnkbTqm/download-ixid.jpg", Title: "Ipsum", }, {id: 3, Image: "https://i.ibb.co/CnkbTqm/download.png", Title: "lorem", }, ];
document.body.innerHTML = categories.reduce((prev, cat) => prev + `<h2>${cat.Title}</h2> <img src='${cat.Image}' />`, '');
We can get the same idea/behaviour/output using map() and join() instead off reduce() like so:
const categories = [{id: 1, Image: "https://i.ibb.co/CnkbTqm/download-ixid-Mnwx.jpg", Title: "Lorem", }, {id: 2, Image: "https://i.ibb.co/CnkbTqm/download-ixid.jpg", Title: "Ipsum", }, {id: 3, Image: "https://i.ibb.co/CnkbTqm/download.png", Title: "lorem", }, ];
document.body.innerHTML = categories.map(cat => `<h2>${cat.Title}</h2> <img src='${cat.Image}' />`).join('');
you can use createElement() api https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
here are some ideas in vanilla
var div = document.createElement('div');
var link = document.createElement('a');
var article = document.createElement('article');
and here in Jquery
var elem = $('<div></div>')
you can create element and append to the them container.

Strange behaviour of Object.keys(Obj[0]).map() :[] function

I have this code in running, here I have given hard coded [0] value,
using this data in <MaterialTable columns={Here using data for table} />,
it should render only first row, but instead its rendering all rows after giving hardcoded 0,
How this result into rendering all columns with all rows and iterating two time?
previewData = [
{id: 1, name: "FileName", size: 15690, type: "doc", Date: "DDMMYY"}
{id: 2, name: "FileName", size: 15690, type: "doc", Date: "DDMMYY"},
{id: 3, name: "FileName", size: 15690, type: "doc", Date: "DDMMYY"},
{id: 4, name: "FileName", size: 15690, type: "doc", Date: "DDMMYY"}
]
Object.keys(props.previewData[0]).map((x) => {
if(x=="id"){return <div>{x}</div>}
) : []
Strange working code:
const columns = () => {
return (
props.previewData && props.previewData.length > 0 ?
Object.keys(props.previewData[0]).map((x) => {
if (props.Table && props.Table !== "nameTable") {
if (x === "id"){
return ({
title: ,
field: x,
width: "500rem",
sorting: false
})// similar code for other fields also
// this code should gets called only once for array [0], its get iterated over from `.forEach()` from data of MaterialTable How???
}
return (
<MaterialTable
title = ""
icons={}
options={{
}}
columns={columns()}
data={
props.previewData && props.previewData.length > 0 ? props.previewData.map((row) => {
const eachRowData = {};
Object.keys(row).forEach((y) =>{
})
return eachRowData;
}) : []
}
/>
Object.keys(props.previewData[0]) with your example previewData is:
["id", "name", "size", "type", "Date"]
Then you map those values to:
[<div>id</div>, undefined, undefined, undefined, undefined]
because you've used map (not filter) and returned something only when the x parameter's value is "id", which is only true for one of the five elements in the array.
It's not clear to me from your question what you want to do instead, but that's why you're getting an array with five elements rather than one. It almost seems like you're trying to filter so you only produce one div, but you're filtering based on a hardcoded value ("id") which would mean you wouldn't need map at all, you'd just do:
<div>{props.previewData[0].id}</div>

How to pass one entry from a list as prop in Vue

Lets say I have the following list in data:
data: {
todos: [
{ id: 1, title: "Learn Python" },
{ id: 2, title: "Learn JS" },
{ id: 3, title: "Create WebApp" }
]
}
Now I want to pass only the entry with id of 2 to the prop:
<dynamic-prop :id=todos[2] :title="todos.title"> </dynamic-prop>
Is something like that possible in Vue?
Sure, you can pass any data on. Just don't forget to add quotation marks and mind the off-by-one problem. So if you want to pass the second element in a (zero-indexed) array, you'd write something like:
<dynamic-prop :id="todos[1].id" :title="todos[1].title"> </dynamic-prop>

Checkbox value wrong behavior on buefy

I generate a list of checkboxes from an array, and when I select some of the box, the v-model value (a generated array declared when Vue instance is created) doesn't add the new box value to the array but replace empty the array and place the value in it.
With an example : I got 3 values "Cat", "Dog" and "Bird". When I check "Cat", the array looks like that ["Cat"] and when I check "Dog" with "Cat", the array looks like that ["Dog"].
When I use a variable (array) defined in the data it works, but when I use my array in the form variable it doesn't work.
<div id="root">
<b-checkbox
v-for="(field, key) in query.fields"
v-model="form[query.id+'-'+query.priority]"
:native-value="field.id">
{{ field.name }}
</b-checkbox>
</div>
<script>
const vue = new Vue({
el: '#root',
data: {
query: {id: 1, priority: 1, fields: [{id: 1, name: 'cat'}, {id: 2, name: 'dog'}, {id: 3, name: 'bird'}]),
form: {},
},
created: function () {
this.form[this.query.id+'-'+this.query.priority] = [];
}
});
</script>
To solve the behaviour of my object, I transformed it into an array using the id of the query as the id of the form. I fetch the priority of the query elsewhere.
It solves my problem but not the problem of using a string as an index for this kind of code.
<div id="root">
<b-checkbox
v-for="(field, key) in query.fields"
v-model="form[query.id]"
:native-value="field.id">
{{ field.name }}
</b-checkbox>
</div>
<script>
const vue = new Vue({
el: '#root',
data: {
query: {id: 1, priority: 1, fields: [{id: 1, name: 'cat'}, {id: 2, name: 'dog'}, {id: 3, name: 'bird'}]),
form: [],
},
created: function () {
this.form[this.query.id] = [];
}
});
</script>

Categories