Iterate over an array of objects with object having multiple properties - javascript

I'm trying to iterate over the following:
state = {
products : [
{
x : "sd",
y : "fdg"
}, {
x : "sdx",
y : "fdgx"
}
]
}
I need to iterate over the above products array and inside object to create:
<tr><td>sd</td><td>fdg</td></tr>
I tried using :
{
this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
})
}
but get multiple errors and prod being undefined.

It's possible that logic elsewhere in your component is mutating the state, which in turn may be the root cause of the error thrown during rendering.
Be sure to check that the products array is consistently defined in your components state, and that the items in that array are defined.
One solution here might be to take a more defensive approach to rendering your table row elements, by doing the following:
{
Array.isArray(this.state.products) && this.state.products
.filter(product => !!product)
.map(product => {
return <tr><td>{product.x}</td><td>{product.y}</td></tr>;
})
}
Here, the rendering logic asserts that this.state.products is the expected array type via Array.isArray(). Addtionally, the logic ensures any prop being rendered is defined by first filtering out any undefined prop items via this line:
filter(product => !!product)
Hope that helps!

The problem is that the return statement is an HTML code which is causing the problem whereas you can encode the code into a string and the DOM will treat it as HTML code
this.state.products.map(function(prod){ return "<tr><td>"+prod.x+"</td><td>"+prod.y +"</td> </tr>" }).

you need to add that in one variable return as below:
const prod = this.state.products.map(function(prod) {
return <tr><td>{prod.x}</td><td>{prod.y}</td></tr>;
});
Use the variable inside render lifecycle as below.
{prod}
Here is the working code attached in jsFiddle
Hope this helps!

Related

I'm not able to render an object property that's inside an array

I'm trying to access the array values of this without much luck. I'm trying to access the label inside food. I've tried it doing this {data.parsed[0].label} but I'm getting an error 0 of undefined whenever I try this. Why can that be if that's how you access an object?
{
"text":"apple"
"parsed":[
0:{
"food":{
"foodId":"food_a1gb9ubb72c7snbuxr3weagwv0dd"
"uri":"http://www.edamam.com/ontologies/edamam.owl#Food_apple"
"label":"Apple"
"nutrients":{...}
"category":"Generic foods"
"categoryLabel":"food"
"image":"https://www.edamam.com/food-img/42c/42c006401027d35add93113548eeaae6.jpg"
}
}
]
"hints":[...]
"_links":{
"next":{...}
}
}
I assume that the component is trying to render before the data is available. Try adding a check to ensure that the data is available:
{data && data.parsed && data.parsed[0] && data.parsed[0].label}
...or if you can use optional chaining:
{data?.parsed?.[0]?.label}

How made a deep copy that can resolve a problem with a table

Im using the table material-table, where the set a new element to my object, called tableData. So that feature create an issues to my code and API because I update using Patch. I implemented a conventional and also custom deep copy of my object to avoid the table add this element to my object.
But for some reason it isnt working. This is an example of the table where you can see how it added the tableData to the example object. https://codesandbox.io/s/lx2v9pn91m Please check the console
Above I showed the real object, and in the element 5 array, appear the tableData after each render. Extra comment, the property of the table I passed to table is: data={MyRealObject.element5}
This is the struct of my real object:
MyRealObject{
element1: boolean,
element2: boolean ,
element3: Array ,
element4: Array ,
Cards: Array ,
}
Card{
Id: number ,
CardNumber : number ,
CardFormat : {CardFormatObject},
//here where appear the tableData after each render
}
CardFormatObject{
Id: number ,
CardNumberLength : number ,
CardFormatLength : number ,
Default:boolean ,
Name: string ,
}
This is the lastest custom deep copy I did and didnt work:
deepcopy(MyRealObject:MyRealObject):MyRealObject{
let salvaCard=[]
for(let i=0;i<user.Cards.length;i++){
salvaCard[i]=this.deepCardCopy(MyRealObject.Cards[i])
}
return{
element1: MyRealObject.element1,
element2: MyRealObject.element2,
element3: [...MyRealObject.element3], //I know here is not deep but isnt important now, and it isnt affected
element4: [...MyRealObject.element4],
Cards: salvaCard,
}as MyRealObject
}
public deepCardCopy(card:Card):Card{
return {
Id:card.Id,
CardNumber:card.CardNumber,
CardFormat:{...card.CardFormat}
} as Card;
}
//////////////////////////////
This are others deep code that I used and dont works, I share to save you time:
--------old solution 1(i dont like it, you can lose element if there are null)------------------------------------------------
// Cards: JSON.parse(JSON.stringify(MyRealObject.Cards)),
---------old solution 2-------------------------------------------
// MyRealObject.Cards.map(card=>{
// const {tableData,...record}=card
// return record
// }),
setState is an async function. That means you won't see the result right after calling it. You have to wait for setState to be finished. There is a callback-Parameter in setState which you can use to get the result of this.state
handleSelectedID = rowData => {
const selectedID = rowData.map(item => item.id);
this.setState({ selectedID }, () => {
// setState is async
// this is the callback
console.log(this.state.selectedID);
});
};
I changed your example-code a bit to give you an example of it: https://codesandbox.io/s/1olz33pmlj
I found a solutions, it is passsing the clone in the moment i passed the object to the table, for example:
data={MyRealObject.map(x => Object.assign({}, x))}
The issues was, that the people that create the table didnt make a clone to the data, the use the same reference, so it will be better do this to avoid problems that some one in the component didnt clone the object.
Kindly, noted, DONT used this way
data={...MyRealObject} or data={Object.assign({}, MyRealObject)}
The CORRECT is:
data={MyRealObject.map(x => Object.assign({}, x))}
Both expression look similiar but it isnot the same.

The value of one input is affecting another

I have a simple app which allows someone to add a numbers into an input, and have those numbers render onto the page (as inputs) which can be edited.
addSiblingValue(evt) {
this.setState({
currentObject: {
...this.state.currentObject,
numberOfSiblings: evt.target.value
}
});
add() {
const array = [...this.state.array, this.state.currentObject];
this.setState({
array
});
}
siblingCountChange(rowIndex, event) {
const array = [...this.state.array];
array[rowIndex].numberOfSiblings = event.target.value;
this.setState({ array });
}
So when I add a number it renders a new input with the value set to the number I've just added, but when I go to change that value, it now is affecting the first input.
The first row of inputs are using their own object currentObject which pushes to to the this.state.array, so I'm not sure why editing anything in that array would affect the currentObject?
Expected behaviour:
User enters a number into the input and clicks add
That input is rendered and can be edited independently
How do I achieve this or what is it I'm doing wrong here?
CodeSandbox
Thank you
When you add this.state.currentObject to the array, it works as an reference, so that the added object in the array and this.state.currentObject are the same object. You can prevent that by adding not the object itself, but a copy of the object into the array:
add() {
const array = [...this.state.array, {"numberOfSiblings": this.state.currentObject.numberOfSiblings}];
this.setState({
array
});
}
siblingCountChange(rowIndex, event) {
const array = [...this.state.array];
array[rowIndex].numberOfSiblings += parseInt(event.target.value);
this.setState({ array });
}
You were not adding the actual number to the current state. I also removed the value from the add like so:
<input
type="text"
onChange={this.siblingCountChange.bind(this, rowIndex)}
/>
You will need to put error handling on the state as a string plus a number leads to NaN error. As you can see the number is parsed before addition.
Thanks to dieckie for pointing me in the right direction. Unfortunately that particular solution did not work, but using Object.assign to create a reference and pushing that to the array did?
Posting here so it helps others/myself in future:
add() {
let copyOfCurrentObject = Object.assign({}, this.state.currentObject);
const array = [...this.state.array, copyOfCurrentObject];
this.setState({
array
})
}
This question was also helpful.

Redux reducer failing to remove array element

I'm having problems trying to get my reducer to work correctly in Redux. I'm new to Redux so I might be missing something simple, but I've played with it for a while and can't figure out what's going wrong.
Here is my process:
Define argument:
First I define the index value that I need. When logged, this returns the correct number...
const thisCommentIndex = parseInt(comments.indexOf(comment))
Function Call:
<div onClick={this.props.removeComment.bind(null, thisCommentIndex)}></div>
Action:
export function removeComment(index) {
return {
type: 'REMOVE_COMMENT',
index
}
}
Reducer:
function comments(state = [], action) {
switch(action.type) {
case 'REMOVE_COMMENT' :
console.log('removing comment with index of ' + action.index)
return [
...state.slice(0, action.index), // why isn't this working???
...state.slice(action.index)
]
default :
return state
}
return state;
}
When I console.log('removing COMMENT with index of ' + action.index), it logs the action.index correctly, the integer I would expect. But the function doesn't remove the element as expected.
Strangely, if I simply pass the array index instead, it works fine (removes the array element). (I would just do this, but due to the way I have set up my app it won't work in this case).
Am I missing something here? Any help appreciated.
You're missing a +1...
return [
...state.slice(0, action.index),
...state.slice(action.index + 1) // <--- need to actually skip what you want to remove
]
#Jack is correct. Another option would be to use Array.filter instead:
return state.filter( (item, index) => index !== action.index)
You might be interested in the new Structuring Reducers section of the Redux docs. In particular, the page on Immutable Update Patterns has some related examples.
If you want to remove multiple items, then you could work through your array backwards
for (var i = this.props.items.length -1; i >= 0; --i) {
if(this.props.items[i]["selected"]) {
this.props.deleteSelectedItem(i);
}
}

Use Lodash to find the indexOf a JSON array inside of an [] array

I have an array that looks something like this.
Users : {
0 : { BidderBadge: "somestuff", Bidders: 6, }
1 : { BidderBadge: "somemorestuff", Bidders: 7,}
}
I want to search the array using lodash to find a value inside of each of the user objects.
Specifically, I want to use values from another similar array of objects to find the value.
var bidArray = [];
_.each(this.vue.AllUsers, function(user) {
_.each(this.vue.Bids, function(bid) {
if(user.BidderBadge == bid.Badge) {
bidArray.push(user);
}
});
});
This is what I have and it works, but I want to do it using only one loop instead of two. I want to use something like _.indexOf. Is that possible?
If you want to avoid nesting, you just have to modify Azamantes' solution a bit
var bidders = this.vue.Bids.reduce(function(acc, bid) {
return acc[bid.BidderBadge] = true;
}, {});
var bidArray = this.vue.AllBidders.filter(function(bidder) {
return !!bidders[bidder.Badge];
});
It is difficult to give an accurate answer with an example that doesn't coincide with the input that your provide.
Anyway, supposing your data structures were more or less like this ones, you could solve the problem with lodash _.intersectionWith.
Intersect both arrays using a comparator that checks the correct object properties. Also, take into account that users must go first in the intersection due to the fact that you're interested in its values.
function comparator(user, bid) {
return user.BidderBadge === bid.Badge;
}
console.log(_.intersectionWith(users, bids, comparator));
Here's the fiddle.

Categories

Resources