Jakub Matwiejew
Jakub Matwiejew

Reputation: 107

Conditional rendering of a mapped nested object

I'm trying to set a conditional rendering of a mapped nested object. Mapped object:

const products = [
    {category: "Napoje", products:
        {
            product1: "Piwo",
            product2: "Wino",
            product3: "Cola",
            product4: "Sprite"
        }
    },
    {category: "Pieczywo", products:
        {
            product1: "Chleb",
            product2: "Bułka",
            product3: "Chałka",
            product4: "Rogal"
        }
    }
]

My code:

class Products extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        for(var i=0; i<this.props.products.length; i++){
            if (Object.values(this.props.products[i].products).indexOf(this.props.filter)<1) return false;
        }
        return(
            <div>
                {this.props.products
                    .map(product =>
                        <div>
                            {product.category}
                            {Object.values(product.products).map(name => <li>{name}</li>)}
                        </div>
                    )
                }
            </div>
        )
    }
}

How can I fix that? The component recives text from search input. The idea is to show only objects that contains the text form input.

Thanks,

Kuba

Upvotes: 0

Views: 904

Answers (3)

margaretkru
margaretkru

Reputation: 2781

Here is a slightly optimized version where objects are mapped to objects with only matched products or nulls and then filtered out for only non-nulls:

class Products extends React.Component {
  getFilteredProducts() {
    const filter = this.props.filter;

    const filteredProducts = this.props.products.map(obj => {
        // filter out those that don't match
        const filtered = Object.values(obj.products)
           .filter(p => p.indexOf(filter) > -1);

        // if no match found, exclude from the list entirely
        if (filtered.length === 0) return null;

        // combine into a new object with only filterd products
        return { ...obj, ...{ products: filtered } };
      })
      // filter out objects without any matched products
      .filter(product => product);

      return filteredProducts;
  }
  render() {
    return (
      <div>
        {this.getFilteredProducts()
          .map((product, ind) =>
            <div key={ind}>
              {product.category}
              {Object.values(product.products).map(name => <li>{name}</li>)}
            </div>
          )
        }
      </div>
    )
  }
}

Upvotes: 1

simbathesailor
simbathesailor

Reputation: 3687

class Products extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        return(
            <div>
                {this.props.products
                    .map(product =>
                        <div>
                            {product.category}
                            {Object.values(product.products).map((name) =>{
if(name.indexOf(this.props.filter) === -1) {
return null
}
return (<li>{name}</li>)
} )}
                        </div>
                    )
                }
            </div>
        )
    }
}

This may help

Upvotes: 0

Andy Ray
Andy Ray

Reputation: 32076

When you need to select only certain elements from an array, a common and good pattern is to filter the array. Here's unoptimized code:

class Products extends React.Component{
    constructor(props){
        super(props)
    }
    render(){
        return(
            <div>
                {this.props.products
                    .filter(product => Object.values(product.products).filter(
                        productName => productName.indexOf(this.props.filter) === -1 )
                    )
                    .map(product =>
                        <div>
                            {product.category}
                            {Object.values(product.products).map(name => <li>{name}</li>)}
                        </div>
                    )
                }
            </div>
        )
    }
}

I would abstract out the filtering function into a method:

class Products extends React.Component{
    constructor(props){
        super(props)
    }

    // Return every product name in a category that doesn't match a
    // specified string
    filterProducts(products, testString) {
        return products.filter(product =>
            Object.values(product.products).filter(
                productName => productName.indexOf(testString) === -1
            )
        );
    }

    render(){
        const { products, filter } = this.props;
        return(
            <div>
                {this.filterProducts(prodcts, filter).map(product => (
                    <div>
                        {product.category}
                        {Object.values(product.products).map(name => <li>{name}</li>)}
                    </div>
                )}
            </div>
        )
    }
}

Upvotes: 0

Related Questions