5

I have data in JSON format in which I need to perform search. There are different tags available and when I click on them it searches in JSON and returns the items which has those tags. For this I am using a js function. It works correctly for the first time but When I push second filter in the function it returns wrong data.

Available Filters are:

Binding

  • Paperback
  • Hardcover
  • Audiobook
  • Boxed Set

Category

  • Classic Rock
  • Pop
  • Pop Rock
  • Electro Pop
  • Soft Rock
  • Rock

Language

  • German
  • English
  • French

Author

  • Male
  • Female
  • Male/Female

Here is the JSON and code I am using:

var m = {
      "Books": [{
          "title": "Book 1",
          "binding": "paperback",
          "category": "pop",
          "language": "english",
          "author": "male"
        },
        {
          "title": "Book 2",
          "binding": "hardcover",
          "category": "pop rock,electro pop",
          "language": "french",
          "author": "female"
        },
        {
          "title": "Book 3",
          "binding": "audiobook",
          "category": "soft rock",
          "language": "german",
          "author": "male,female"
        },
        {
          "title": "Book 4",
          "binding": "boxed set",
          "category": "rock,classic rock",
          "language": "english",
          "author": "female,male"
        },
        {
          "title": "Book 5",
          "binding": "paperback",
          "category": "electro pop,rock,classic rock",
          "language": "french",
          "author": "male/female"
        },
        {
          "title": "Book 6",
          "binding": "paperback",
          "category": "rock",
          "language": "french",
          "author": "male"
        }
      ]
    }


    // a function which accepts key which is one of binding,category,language,author.
    // the array will be filtered on this key
    function getFilteredElement(key, value) {
        var bookFilter = [];
        m.Books.forEach(function(item){
           var getFilterField = item[key];
           // since the value is a string, so splitting it and creating an array
           var keyArray = item[key].split(',');
           // now checking if the passed value has a presence in the  above array
           if (keyArray.indexOf(value) !== -1) {
               // if present pushed the book name
               bookFilter.push(item.title);
           }
       });
        // returning the array of books
        return bookFilter;
    }

    console.log(getFilteredElement('category', 'rock'))

For e.g. When I push category = rock it returns Book 4, Book 5 and Book 6 but If I push category = rock and language = french, the returned result should only be Book 5 but it doesn't return correct results.

Could anyone please help.

9
  • I works to me, I don't know if your are calling the function from different places Commented Dec 13, 2017 at 11:26
  • @David If I push category = rock and language = french, the returned result should only be Book 5 but it doesn't return correct results. Commented Dec 13, 2017 at 11:29
  • These are my outputs for those calls: [ 'Book 4', 'Book 5', 'Book 6' ] [ 'Book 2', 'Book 5', 'Book 6' ] I think they are right Commented Dec 13, 2017 at 11:38
  • btw, JSON is a string. what you have is an array with objects. Commented Dec 13, 2017 at 11:38
  • Maybe what you want is to combine both filters? Commented Dec 13, 2017 at 11:38

3 Answers 3

5

You could use an object for filters and take the keys the key to search for and the value as excact value for the string.

Properties with commas are splitted and checked.

function getBooks(filters) {
    return array.Books.filter(function (o) {
        return Object.keys(filters).every(function (k) {
            return o[k].split(',').some(function (v) {
                return v === filters[k];
            });
        });
    });
    //.map(function (o) {
    //    return o.title;
    //});
}

var array = { Books: [{ title: "Book 1", binding: "paperback", category: "pop", language: "english", author: "male" }, { title: "Book 2", binding: "hardcover", category: "pop rock,electro pop", language: "french", author: "female" }, { title: "Book 3", binding: "audiobook", category: "soft rock", language: "german", author: "male,female" }, { title: "Book 4", binding: "boxed set", category: "rock,classic rock", language: "english", author: "female,male" }, { title: "Book 5", binding: "paperback", category: "electro pop,rock,classic rock", language: "french", author: "male/female" }, { title: "Book 6", binding: "paperback", category: "rock", language: "french", author: "male" }] };

console.log(getBooks({ category: 'rock' }));
console.log(getBooks({ category: 'rock', language: 'french' }));
.as-console-wrapper { max-height: 100% !important; top: 0; }

Sign up to request clarification or add additional context in comments.

9 Comments

Please allow me 5 minutes so that I could check it for all available conditions.
I was going to suggest this next. You need a stricter comparison mechanism ===. includes() and indexOf() aren't strict enough for what you want.
Right. This is working as per my needs. I am implementing this in my code.
Your code works perfect. But I get an error when Cannot read property 'split' of undefined when I try to push the tags with help of a function. The code I am using is: var runSearch = function() { var query = []; var keys = []; var count = 0; for (var i in searchTags) { for (var j = 0; j < searchTags[i].length; j++) { //category: 'rock' query.push(i + ": '" + searchTags[i][j] + "'"); } } var queryString = query.join(' , '); console.log('{' + queryString + '}'); return '{' + queryString + '}'; }
This returns filters: "{category: 'rock' , author: 'male'}" on console.log('filters: ' + JSON.stringify(filters)); and returns k : 0 on console.log('k : ' + k); But when I use static value in function like getBooks({ category: 'rock', language: 'french' }) this returns filters: "{author: 'male'}" and k : author
|
2

Use can utilize Array.prototype.filter(), Array.prototype.reduce(), and String.prototype.includes() to create such a filtration system, wherein an array of filters consisting of key value pairs can progressively be added to in order to refine an array of books.

See below for a practical example.

// Books.
const books = [
  {
    "title": "Book 1",
    "binding": "paperback",
    "category": "pop",
    "language": "english",
    "author": "male"
  },
  {
    "title": "Book 2",
    "binding": "hardcover",
    "category": "pop rock,electro pop",
    "language": "french",
    "author": "female"
  },
  {
    "title": "Book 3",
    "binding": "audiobook",
    "category": "soft rock",
    "language": "german",
    "author": "male,female"
  },
  {
    "title": "Book 4",
    "binding": "boxed set",
    "category": "rock,classic rock",
    "language": "english",
    "author": "female,male"
  },
  {
    "title": "Book 5",
    "binding": "paperback",
    "category": "electro pop,rock,classic rock",
    "language": "french",
    "author": "male/female"
  },
  {
    "title": "Book 6",
    "binding": "paperback",
    "category": "rock",
    "language": "french",
    "author": "male"
  }
]

// Query.
const query = (books, filters) => {

  // filters = [{key: 'category', value: 'string'}..]

  return books.filter((book) => {

    // Found?
    return filters.reduce((found, filter) => {
      if (!(book[filter.key].includes(filter.value))) return false
      return found
    }, true)

  })

}

// Log.
console.log('Category = Rock', query(books, [{key: 'category', value: 'rock'}]))
console.log('Category = Rock + Language = French', query(books, [{key: 'language', value: 'french'}]))

console.log('Paperback', query(books, [{key: 'binding', value: 'paperback'}])) // Paperback.
console.log('Paperback + Male', query(books, [{key: 'binding', value: 'paperback'}, {key: 'author', value: 'male'}])) // Paperback + Male.
console.log('Paperback + Male + Pop', query(books, [{key: 'binding', value: 'paperback'}, {key: 'author', value: 'male'}, {key: 'category', value: 'pop'}])) // Paperback + Male + Pop.

3 Comments

Looks Nice. I am checking the code for all available conditions.
Aah yep I see what you mean though. You want the filtration to be stricter. ie. 'rock' != 'soft rock'.
Yes. While testing I used console.log('multiple category', query(books, [{key: 'category', value: 'rock'}])) This returns Book 2,3,4,5 and 6 but only Book 4,5 and 6 contains Rock
0

This approach solve your problem:

function applyFilters(books, filters) {
    for(let filter of filters) {
        var key = filter[0];
        var value = filter[1];
        books = getFilteredElement(books, key, value);
    }

    return books.map(book => book.title);
}

// a function which accepts key which is one of binding,category,language,author.
// the array will be filtered on this key
function getFilteredElement(books, key, value) {
    var bookFilter = [];
    books.forEach(function(item){
       var getFilterField = item[key];
       // since the value is a string, so splitting it and creating an array
       var keyArray = item[key].split(',');
       // now checking if the passed value has a presence in the  above array
       if (keyArray.indexOf(value) !== -1) {
           // if present pushed the book name
           bookFilter.push(item);
       }
   });
    // returning the array of books
    return bookFilter;
}

var filtersToApply = [["category", "rock"], ["language", "french"]];
var bookFilter = applyFilters(m.Books, filtersToApply);
console.log(bookFilter);

1 Comment

Please allow me 5 minutes so that I could check it for all available conditions.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.