根据多个条件和多个数组进行过滤?

时间:2019-06-13 23:53:54

标签: javascript vue.js filter filtering

我有这样的JSON产品列表:

{
    "products": [
        {
            "tags": ["filter-color-White", "filter-style-Low 1", "5", "6", "7", "8", "9", "10", "11", "12", "13"],
            "styles": ["filter-style-Low 1"],
            "colors": ["filter-color-White"],
            "sizes": ["5", "6", "7", "8", "9", "10", "11", "12", "13"]
        }, 
        {
            "tags": ["filter-color-Black", "filter-color-Red", "filter-color-Blue", "filter-style-Boot", "7", "8", "9", "12", "13"],
            "styles": ["filter-style-Boot"],
            "colors": ["filter-color-Black", "filter-color-Red", "filter-color-Blue"],
            "sizes": ["7", "8", "9", "12", "13"]
        }, 
        {
            "tags": ["filter-color-Black", "filter-color-Red", "filter-style-Gat", "7", "8", "9", "10", "11", "12", "13"],
            "styles": ["filter-style-Gat"],
            "colors": ["filter-color-Black", "filter-color-Red"],
            "sizes": ["7", "8", "9", "10", "11", "12", "13"]
        }
        ...
        ...
        ...
    ]
}

如您所见,有stylescolorssizes。还有一个tags项实际上是由之前所有这三个项组成的。

我需要有人能够根据多项选择进行过滤。如图所示,如果有人选择Low 1样式,然后选择黑色,则向他们显示该样式中的黑色项目。但是,如果他们也选择白色,则将它们显示为白色或黑色,或同时显示两者。与尺寸选择相同。示例:黑色,白色,AND尺寸5或6或两者都低的1件商品。

最后,应该一次选择多个样式,并且还应该选择颜色和大小,而不选择样式。所以上面的示例,但是然后在顶部添加另一个样式,例如Boot样式。然后,它将返回符合所有条件的产品。

这甚至可行吗?

目前我正在这样做:

let filterOptions = this.selectedOptions;
this.products.forEach(product => {
  if (filterOptions.some(item => product.tags.includes(item))) {
    return product.visible = true;
  } else if(filterOptions.length == 0) {
    return product.visible = true;
  } else {
    return product.visible = false;
  }
})

其中product.visible只是一个简单的布尔值,它允许vuejs在页面上显示或不显示项目,而this.selectedOptions是一个数组,每次有人在过滤器中添加/删除选项时,该数组就会动态填充。想要的示例:

this.selectedOptions = ["filter-style-Erving","filter-color-Black","filter-color-Brown","8","9"]

以上过滤代码有效,但不可靠。它返回与任何条件匹配的所有项目,而与其他选定过滤器无关。如果我将some更改为every,则会发生相反的情况,即尝试仅查找具有红色和黑色的项目。或5 AND 6尺寸,等等

我或多或少尝试在https://www.everlane.com/collections/mens-tees上复制过滤条件

2 个答案:

答案 0 :(得分:1)

您可能会发现这很有用。

Vue.config.productionTip = false;
Vue.config.devtools = false;

new Vue({
  el: '#hook',
  template: '#app-template',
  data: () => ({
    data: [{
        styles: ["filter-style-Low 1"],
        colors: ["filter-color-White"],
        sizes: ["5", "6", "7", "8", "9", "10", "11", "12", "13"]
      },
      {
        styles: ["filter-style-Boot"],
        colors: ["filter-color-Black", "filter-color-Red", "filter-color-Blue"],
        sizes: ["7", "8", "9", "12", "13"]
      },
      {
        styles: ["filter-style-Gat"],
        colors: ["filter-color-Black", "filter-color-Red"],
        sizes: ["7", "8", "9", "10", "11", "12", "13"]
      }
    ],
    filters: {
      styles: [],
      colors: [],
      sizes: []
    },
    additiveFiltering: false
  }),
  computed: {
    products() {
      return this.data.map(product => ({
        ...product,
        tags: this.tags(product)
      }))
    },
    filteredProducts() {
      return this.products.filter(
        this.additiveFiltering
        ? p => this.x(this.filters.styles, p.styles).length
            || this.x(this.filters.colors, p.colors).length
            || this.x(this.filters.sizes, p.sizes).length
        : p => (!this.filters.styles.length
                || this.x(this.filters.styles, p.styles).length)
            && (!this.filters.colors.length
                || this.x(this.filters.colors, p.colors).length)
            && (!this.filters.sizes.length
                || this.x(this.filters.sizes, p.sizes).length)
            
      )
    },
    allStyles() {
      return this.getAll('styles')
    },
    allColors() {
      return this.getAll('colors')
    },
    allSizes() {
      return this.getAll('sizes')
    }
  },
  methods: {
    tags(product) {
      return [].concat(product.styles, product.colors, product.sizes)
    },
    logger(obj) {
      return JSON.stringify(obj, null, 2)
    },
    getAll(prop) {
      return [ ...new Set([].concat.apply([], this.data.map(item => item[prop])))]
    },
    x(arr1, arr2) {
      return arr1.filter(val => arr2.includes(val))
    }
  }
})
ul>li>span {
  background-color: #333;
  color: white;
  padding: 0 5px 2px;
  margin: 0 5px 5px 0;
  border-radius: 3px;
  font-variant: all-petite-caps;
  font-family: monospace;
}
.filters {
  display: flex;
}
.filters > div {
  flex: 1;
}
.filters > div:last-child {
  columns: 2;
}
.filters > div:last-child div{
  column-span: all;
}
.filters label {
  display: block;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  margin: 5px;
  border: 1px solid transparent;
  padding: 1rem;
}
.selected {
  background-color: #f5f5f5;
  border-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script type="text/template" id="app-template">
  <div id="app">
    <div class="filters">
      <div>
        <div>Style</div>
        <label v-for="style in allStyles" :key="style">
          <input type="checkbox" v-model="filters['styles']" :value="style">
          {{style}}
        </label>
      </div>
      <div>
        <div>Colors</div>
        <label v-for="color in allColors" :key="color">
          <input type="checkbox" v-model="filters['colors']" :value="color">
          {{color}}
        </label>
      </div>
      <div>
        <div>Sizes</div>
        <label v-for="size in allSizes" :key="size">
          <input type="checkbox" v-model="filters['sizes']" :value="size">
          {{size}}
        </label>
      </div>
    </div>
    Additive filtering: <input type="checkbox" v-model="additiveFiltering">
    <h3>Products</h3>
    <ul>
      <li v-for="(product, key) in products" :key="key" 
      :class="{selected: filteredProducts.includes(product)}">
        <span v-for="tag in product.tags" :key="tag" v-text="tag"></span>
      </li>
    </ul>
    <pre v-text="{filters}"></pre>
  </div>
</script>
<div id="hook"></div>

相关的位是x方法,它是数组的交集。

答案 1 :(得分:0)

此过滤器实际上是打开的。

但是我认为您正在寻找的东西中有些项目是 AND ,而另一些项目是 OR

因此,如果用户选择了大小,则您希望对其进行过滤。

但是颜色||选项吗?

如果是这样,我将更正该模型。 让我知道。

const p = {
    "products": [
        {
            "tags": ["filter-color-White", "filter-style-Low 1", "5", "6", "7", "8", "9", "10", "11", "12", "13"],
            "styles": ["filter-style-Low 1"],
            "colors": ["filter-color-White"],
            "sizes": ["5", "6", "7", "8", "9", "10", "11", "12", "13"]
        }, 
        {
            "tags": ["filter-color-Black", "filter-color-Red", "filter-color-Blue", "filter-style-Boot", "7", "8", "9", "12", "13"],
            "styles": ["filter-style-Boot"],
            "colors": ["filter-color-Black", "filter-color-Red", "filter-color-Blue"],
            "sizes": ["7", "8", "9", "12", "13"]
        }, 
        {
            "tags": ["filter-color-Black", "filter-color-Red", "filter-style-Gat", "7", "8", "9", "10", "11", "12", "13"],
            "styles": ["filter-style-Gat"],
            "colors": ["filter-color-Black", "filter-color-Red"],
            "sizes": ["7", "8", "9", "10", "11", "12", "13"]
        }
    ]
};

const getFiltered = ( ({products}, filterValues) =>                                                
                        products.filter(p => Object.entries(p)
                                                   .flatMap(([k, v]) => v)
                                                   .some(entry => filterValues.includes(entry))));

console.log(getFiltered(p, ["5", "filter-style-Gat"]));

假设我们需要一些条件:

const p = {
  "products": [{
      "tags": ["filter-color-White", "filter-style-Low 1", "5", "6", "7", "8", "9", "10", "11", "12", "13"],
      "styles": ["filter-style-Low 1"],
      "colors": ["filter-color-White"],
      "sizes": ["5", "6", "7", "8", "9", "10", "11", "12", "13"]
    },
    {
      "tags": ["filter-color-Black", "filter-color-Red", "filter-color-Blue", "filter-style-Boot", "7", "8", "9", "12", "13"],
      "styles": ["filter-style-Boot"],
      "colors": ["filter-color-Black", "filter-color-Red", "filter-color-Blue"],
      "sizes": ["7", "8", "9", "12", "13"]
    },
    {
      "tags": ["filter-color-Black", "filter-color-Red", "filter-style-Gat", "7", "8", "9", "10", "11", "12", "13"],
      "styles": ["filter-style-Gat"],
      "colors": ["filter-color-Black", "filter-color-Red"],
      "sizes": ["7", "8", "9", "10", "11", "12", "13"]
    }
  ]
};

const getFiltered = (({ products }, {
  tags,
  styles,
  colors,
  sizes
}) => {
  // Filter size fully.
  return products.filter(({
      sizes: s
    }) => (sizes) ? s.some(size => sizes.includes(size)) : true)
    // Now color
    .filter(({
      colors: c
    }) => (colors) ? c.some(color => colors.includes(color)) : true)
    // style etc.
    .filter(({
      styles: s
    }) => (styles) ? s.some(style => styles.includes(style)) : true)

});

const filter = {
  sizes: ["6", "7"],
  colors: ["filter-color-Red"]
};

console.log(getFiltered(p, filter));