Shiju Nambiar
Shiju Nambiar

Reputation: 161

I am trying to render a filtered list of objects

I have a sample list of which is an array of objects with three fields in the App component. The list is passed as a prop to an "booksearch" component which handles the logic to search and filter the list based on search text. below is the JSX which renders the book. I am doubting issue is with the "matcheBook" method.

<div className="output-container">
    {this.props.books
        .filter((e) => this.matchesBook(e))
        .map((b) => (
            <div className="output-card" key={b.title}>
                {Object.entries(b).map(([k, v]) => (
                    <div key={v} className="out-section">
                        <span className="heading">
                            <b>{k}</b>
                        </span>
                       <span className="details">{v}</span>
                    </div>
                ))}
          </div>
       ))}
</div>

method for handling the search text

handleChange(evt, name) {
    let searchText = evt.target.value;
        this.setState((state) => ({
            ...state,
            fields: {
                ...state.fields,
                [name]: searchText
        }
    }));
}

filtering logic

matchesBook(book) {
    const { fields } = this.state;
        return Object.entries(book).some(
            ([k, v]) =>
                !fields[k] ||
                v.toString()
                 .toLowerCase()
                 .includes(fields[k].toString().trim().toLowerCase())
           );
}

State shape

this.state = {
    fields: initialFields
};

"initialFields" comes from below

const fieldsArray = ["author", "title", "year"];

const initialFields = fieldsArray.reduce(
    (a, e) => ({
        ...a,
        [e]: ""
      }),
    {}
 );

codesandbox

Upvotes: 0

Views: 73

Answers (3)

Shiju Nambiar
Shiju Nambiar

Reputation: 161

Here is a working solution where I ran a Array.prototype.every on values of the search object and Array.prototype.some on the book object

matchesBook(book) {
    const { fields } = this.state;
    return Object.values(fields)
        .every(v => v === "") || 
        Object.entries(book)
            .some(([k, v]) => 
                fields[k] !== "" && v.toString()
               .toLowerCase()
               .includes(fields[k].trim().toLowerCase()));
}

Upvotes: 0

Kelvin
Kelvin

Reputation: 56

According to MDN some() method executes the callbackFn function once for each element present in the array until it finds the one where callbackFn returns a truthy value (a value that becomes true when converted to a Boolean). If such an element is found, some() immediately returns true.

In your case !fields[k] always return true when its value is "" and so it never have to compare succeeding values in the array which contains the field we are checking, in this case year. Please see example below.

const book = {
    author: "Charles Dickens",
    title: "The Pickwick Papers",
    year: "1837"
  };

const fields = {author: "", title: "", year: "lookinforthisstringinbook"};

const checkValue = Object.entries(book).some(
    ([k, v]) =>
        !fields[k] || // --> always returns true
        v.toString()
         .toLowerCase()
         .includes(fields[k].toString().trim().toLowerCase())
   );

console.log(checkValue)

Upvotes: 0

Jishnu Raj
Jishnu Raj

Reputation: 220

I've never tried some() function, but doing the search function code on my own way I modified your matchesBook function into this one:

matchesBook(book) {
  const { fields } = this.state;

  let matching = 0;

  for (let i = 0; i < Object.entries(fields).length; i++) {
    if (Object.entries(fields)[i][1] === "") {
      matching++;
    } else {
      if(String(Object.entries(book)[i][1]).toLowerCase().includes(String(Object.entries(fields)[i][1]).toLowerCase())){
        matching++;
      }
    }
  }
  return matching === Object.entries(fields).length;
}

Try it, it'll work!

Upvotes: 2

Related Questions