使用JavaScript的forEach和indexOf搜索JSON中的多个筛选器

5

我有一些以JSON格式存储的数据需要进行搜索。有不同的标签可用,当我点击它们时,它会在JSON中搜索并返回具有这些标签的项目。为此,我使用了一个js函数。它在第一次执行时可以正确地工作,但是当我在函数中添加第二个过滤器时,它会返回错误的数据。

可用的过滤器如下:

装帧方式

  • 平装
  • 精装
  • 有声读物
  • 组合套装

类别

  • 经典摇滚
  • 流行
  • 流行摇滚
  • 电子流行
  • 轻柔摇滚
  • 摇滚

语言

  • 德语
  • 英语
  • 法语

作者

  • 男性
  • 女性
  • 男/女

这里是我正在使用的JSON和代码:

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'))

举例来说,当我推送 category = rock 时,它返回 Book 4, Book 5 和 Book 6,但如果我同时推送 category = rocklanguage = french,则应该仅返回 Book 5,但它并未返回正确的结果。请问有谁能帮忙解决?

@David 如果我推送 category = rocklanguage = french,返回的结果应该只有 Book 5,但它没有返回正确的结果。 - hashtagerrors
这些是我对那些调用的输出:[ '书籍4', '书籍5', '书籍6' ] [ '书籍2', '书籍5', '书籍6' ]我认为它们是正确的。 - David Vicente
顺便提一下,JSON 是一个字符串。你手头的是一个对象数组。 - Nina Scholz
@David 是的,我需要合并过滤器。 - hashtagerrors
我理解了这部分内容,但是不确定如果你在 'rock' 或者 'electro pop,rock,classic rock' 或者 'soft rock' 中搜索 'rock' - Nina Scholz
显示剩余4条评论
3个回答

5
您可以使用一个对象来作为filters,将键用作搜索的关键字,值作为字符串的精确值。

逗号分隔的属性将被拆分并进行检查。

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; }


请给我5分钟时间,以便我能够检查所有可用的条件。 - hashtagerrors
我打算建议这个。你需要一个更严格的比较机制 ===includes()indexOf() 对于你想要的不够严格。 - Arman Charan
你的代码运行得很完美。但是当我尝试使用一个函数来推送标签时,出现了错误Cannot read property 'split' of undefined。 我正在使用的代码是: 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 + '}'; } - hashtagerrors
请添加searchTags的内容。 - Nina Scholz
@NinaScholz i:作者 searchTags:{"author":["男性"]}(是一个对象) searchTags[i][j]:男性 - hashtagerrors
显示剩余4条评论

2
您可以利用Array.prototype.filter(), Array.prototype.reduce()String.prototype.includes()来创建这样一个过滤系统,其中包含一组由键值对组成的过滤器数组,可以逐步添加以精细筛选books数组。
请参见下面的实际示例。

// 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.


看起来不错。我正在检查所有可用条件的代码。 - hashtagerrors
啊,我明白你的意思了。你想要过滤更严格一些,比如说“摇滚”和“轻柔摇滚”是不同的。 - Arman Charan
是的。在测试时,我使用了console.log('multiple category', query(books, [{key: 'category', value: 'rock'}]))。这将返回书籍2、3、4、5和6,但只有书籍4、5和6包含Rock - hashtagerrors

0

这种方法可以解决你的问题:

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
请给我5分钟时间,这样我就可以检查所有可用的条件。 - hashtagerrors

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接