【问题标题】:React Native How to filter a Flatlist with more than 3 parameters?React Native 如何过滤超过 3 个参数的 Flatlist?
【发布时间】:2020-08-27 12:39:20
【问题描述】:

各位程序员,大家好,我是 React Native 新手,所以我会快速总结一下示例和代码,然后再继续。

我从 api 获取产品列表并将其存储在两个数组中:products[]catalogue[]

目录数组用于存储从 api 接收到的所有项目。 products 数组是 Flatlist 用来显示数据的数组。应用过滤器时,应过滤产品中的项目,以便平面列表也更新屏幕上新过滤的数据。

现在这是我从 api 获取的所有对象数据中的一个简化对象。

Object {
  "designNumber": "CHE-S-207 RW",
  "id": 292187,
  "imageUrl": "http://company.shop.org/images/shop/136/4.jpg",
  "itemCategory": "BRACELET",
  "itemStatus": "INSTOCK",
  "itemType": "CHE",
  "quantity": "1",
  "rfidTag": "Item4",
  "shopId": 136,
  "skuNumber": "Item4",
}

我将从对象数据中用于过滤器的主要值是itemStatusitemCategoryitemType

我在状态对象中使用这些变量来存储其中的值,我将使用多选选择器从过滤屏幕 UI 中选择这些值。

state = {
  products: [],
  catalogue: [],
  itemStatus: "", //Binary values. Only one string value for this one
  itemCategory: [], //Multiple values in array
  itemType: [], //Multiple values in array
};

现在我们来谈谈主要交易。假设我已经使用过滤器屏幕 UI 为这些过滤器参数选择了某些值。例如,itemStatus: "AVAILABLE"itemCategory: ["MENS", "WOMENS", "CORPORATE"]itemType: ["COMMON","UNCOMMON", "SALE", "EXCLUSIVE"]

所以用外行的话来说,使用我为状态选择了可用的 UI,为类别选择了 3 个值,为类型选择了 4 个值。现在最后有一个应用过滤器按钮。这是我调用 filterProducts() 之类的函数的地方,它将采用这些多个参数并相应地过滤我的 Flatlist 项目。

请记住,catalogue[] 数组包含从 api 获取的全部数据,products[] 数组是在平面列表中显示的内容。因此,理想情况下,来自 filter 函数的任何新过滤数据都应该更新 products[] 数组,以便我认为也会在 Flatlist 上更新。

我的问题?我在堆栈和媒体上阅读的每篇文章总是只查看搜索栏搜索过滤器示例。基本上只接受1个过滤字符串然后过滤列表。

但是,如果我有多个参数要过滤,就像我试图实现的示例一样?如果您有 3 个参数,并且每个参数可以有多个值(4 个或更多)以在过滤中考虑,该怎么办。如何应用如此复杂的过滤级别,然后将过滤后的项目返回到我的 Flatlist?

<Button title="Apply Filter" onPress={() => this.filterProducts}/>

filterProducts = () => {
  //Logic to take all the filter parameters and their selected values

  this.state.products = if({/** No filters are applied */}){
    return this.state.catalogue //Return original data
  } else {
    this.state.catalogue.filter({/** Apply complex filter logic here}).map(){
      //return filtered items list
    }
  }
}

也许逻辑流程是这样的?除了我不知道如何过滤一个同时存在这么多过滤参数的列表。任何帮助将不胜感激。谢谢。

更新 1:根据 u/TommyLeong 的建议,我更新了我的代码。这是我的过滤器功能和逻辑的样子:

state = {
  isFilterActive: false,
  products: [],
  catalogue: [],
  itemStatus: "",
  itemCategory: [],
  itemType: [],
}

//Get data from the api
fetchProducts = aysnc () => {
  //Make the api call and get the response
  const data = await response.json();

  this.setState({
    products: data.data, //Originally show all the data from the api
    catalogue: data.data, //Store all the data from the api
  });
};

//Filter Function
filterProducts = () => {
  console.log("function called", this.state.isFilterActive);
  //Pack all filter data in one query object
  let query = {
    itemStatus: this.state.itemStatus,
    itemCategory: this.state.itemCategory,
    itemType: this.state.itemType,
  };
  console.log("Filter object", Object.entries(query));

  if (this.state.isFilterActive) {
    const filteredData = this.state.catalogue.filter(function (item) {
      return Object.entries(query).every(([key, value]) =>
        value.includes(item[key])
      );
    });
    console.log("Result: Filtered Array", filteredData);
    this.setState({ products: filteredData });
  } else {
    //Return original data is isFilterActive == false
    this.setState({ products: this.state.catalogue });
  }
};

render(){
  return(
    //Contains my views. My Filter screen has a bunch of pickers 
    //and a apply filter button

    <DropDownPicker 
      label="Item Status"
      multipleSelection={true} //More than one values can be selected
      items={/*define my filter values for itemStatus here*/}
      onChangeItem={(item) => {
        this.setState({ itemStatus: item.value});
      }
    />
    <DropDownPicker 
      label="Item Category"
      items={/*define my filter values for itemCategory here*/}
      onChangeItem={(item) => {
        //item here is an array of selected values
        this.setState({ itemCategory: item});
      }
    />
    <DropDownPicker 
      label="Item Type"
      multipleSelection={true} //More than one values can be selected
      items={/*define my filter values for itemType here*/}
      onChangeItem={(item) => {
        //item here is an array of selected values
        this.setState({ itemType: item });
      }
    />
    <Button 
      title="Apply Filter"
      onPress={() => {
        this.setState({ isFilterActive: true }, () =>
          this.filterProducts()
        );
      }}
  )
}

现在我的代码和逻辑已经很清楚了,让我们继续解决新问题。 过滤器功能中的过滤器逻辑 100% 有效! 但只有当所有 3 个过滤器属性都选择了某个值时

//my filter object in the function
let query = {
  itemStatus: this.state.itemStatus,
  itemCategory: this.state.itemCategory,
  itemType: this.state.itemType,
};

案例 1:console.log 当所有 3 个过滤器类别都选择了一些值时:

Filter object Array [
  Array [
    "itemStatus",
    "INSTOCK",
  ],
  Array [
    "itemCategory",
    Array [
      "MENS",
      "WOMENS",
    ],
  ],
  Array [
    "itemType",
    Array [
      "COMMON",
      "SALE"
    ],
  ],
]

Result: Filtered Array Array[
  Object: {
    //first item matching all 3 filter criteria
  },
  Object: {
    //2nd item matching all 3 filter criteria
  },
  Object: {
    //3rd item matching all 3 filter criteria
  },
]

案例 2: 没有为 3 个过滤条件中的任何一个选择任何值时的 console.log。对于这种情况,我选择 itemType,即 itemType[] 为空白。

Filter object Array [
  Array [
    "itemStatus",
    "INSTOCK",
  ],
  Array [
    "itemCategory",
    Array [
      "MENS",
      "WOMENS",
    ],
  ],
  Array [
    "itemType",
    Array [], //Array is blank here because no filter 
              //values were picked for itemType
  ],
]

Result: Filtered Array Array [] //No items returned to me, Flatlist becomes empty

我当然知道问题出在这个过滤器逻辑上,但我无法摆脱,为什么当 3 个条件之一存在未定义值时过滤器无法正常工作.. 最重要的是,再一次知道如何解决它:(

//Current filter Logic
if (this.state.isFilterActive) {
    const filteredData = this.state.catalogue.filter(function (item) {
      return Object.entries(query).every(([key, value]) =>
        value.includes(item[key])
      );
    });
    console.log("Result: Filtered Array", filteredData);
    this.setState({ products: filteredData });
  } else {
    //Return original data is isFilterActive == false
    this.setState({ products: this.state.catalogue });
  }

【问题讨论】:

    标签: javascript arrays react-native object react-native-flatlist


    【解决方案1】:

    这就是我要解决的问题..

    1. 创建一个变量调用hasFilterApply 来存储用户是否应用了任何过滤器的布尔值。从 UI 中删除所有过滤器时,您需要将布尔值设置回 FALSE。
    2. 每次过滤前,我都会根据hasFilterArray决定对哪个数组(目录/产品)进行过滤。如果为真,则意味着我应该继续使用 this.state.products,因为它包含最新的过滤产品列表。
    3. 渲染时,再次检查hasFilterApply是否为TRUE。如果是,则渲染this.state.products,否则只渲染this.state.catalgoues
    this.state = {
        hasFilterApply: false,      // update hasFilterApply to TRUE when one or more filter is applied, set to FALSE when no filter is applied
        catalogues: [],             // keep original response
        products: []                // this will be the final render product list
    }
    
    /*
    * Assuming this is how your catalgoues will be
    const catalogues = [{"designNumber":"CHE-S-207 A","id":1,"imageUrl":"http://company.shop.org/images/shop/136/4.jpg","itemCategory":"BRACELET","itemStatus":"AVAILABLE","itemType":"EXCLUSIVE","quantity":"1","rfidTag":"Item1","shopId":1,"skuNumber":"Item1",},{"designNumber":"CHE-S-207 B","id":2,"imageUrl":"http://company.shop.org/images/shop/136/4.jpg","itemCategory":"MENS","itemStatus":"NO","itemType":"SALE","quantity":"1","rfidTag":"Item2","shopId":2,"skuNumber":"Item2",},{"designNumber":"CHE-S-207 C","id":3,"imageUrl":"http://company.shop.org/images/shop/136/4.jpg","itemCategory":"WOMENS","itemStatus":"AVAILABLE","itemType":"UNCOMMON","quantity":"1","rfidTag":"Item3","shopId":3,"skuNumber":"Item3",},{"designNumber":"CHE-S-207 D","id":4,"imageUrl":"http://company.shop.org/images/shop/136/4.jpg","itemCategory":"CORPORATE","itemStatus":"AVAILABLE","itemType":"COMMON","quantity":"1","rfidTag":"Item4","shopId":4,"skuNumber":"Item4",},{"designNumber":"CHE-S-207 E","id":5,"imageUrl":"http://company.shop.org/images/shop/136/4.jpg","itemCategory":"MENS","itemStatus":"AVAILABLE","itemType":"COMMON","quantity":"1","rfidTag":"Item5","shopId":5,"skuNumber":"Item5",},]
    */
    
    getFromAPI = async () => {
        const catalogues = await getCatalogues();
        this.setState({ catalogues })
    }
    
    filterStatus = (filterValue) => {
        const latestList = hasFilterApply ? this.state.products : this.state.catalogues;
        const newList = []
    
        latestList.filter((product)=>{
          if(product.itemStatus === filterValue){
            newList.push(product)
          }
        }
    
        this.setState({ products: newList })
    }
    
    filterCategory = (filterValue) => {
        const latestList = hasFilterApply ? this.state.products : this.state.catalogues;
        const newList = []
    
        latestList.filter((product)=>{
          if(product.itemCategory === filterValue){
            newList.push(product)
          }
        }
    
        this.setState({ products: newList })
    }
    
    filterType = (filterValue) => {
        const latestList = hasFilterApply ? this.state.products : this.state.catalogues;
        const newList = []
    
        latestList.filter((product)=>{
          if(product.itemType === filterValue){
            newList.push(product)
          }
        }
    
        this.setState({ products: newList })
    }
    
    renderMyProducts = () => {
        // At the end before you decide render the products, you should know which ARRAY (catalgoues/products) should be passed to your component.
        // Do checking on whehter "hasFilterApply" is TRUE or false. If false, then pass catalgoues, otherwise pass products to your  component.
    }
    
    render(){
        return(
            // render your components
        )
    }
    
    

    【讨论】:

    • 我看到了一个非常有趣的过滤器实现。所以我是否必须为每个过滤器参数之一真正制作 3 个过滤器函数,或者是否可以使用我在原始帖子中提到的 filterProducts() 之类的单个过滤器函数来实现。就像一个函数,但它需要多个参数。只是对任何其他实现方法感到好奇?
    • 除非您知道需要过滤什么,否则您将依赖于一个非常动态的数组列表和过滤器参数。当然,您可以通过 catalogues.filter(prod =&gt; prod.itemStatus === 'AVAILABLE' &amp;&amp; prod.itemType === 'COMMON') 来缩短过滤器,当您确切知道要应用什么过滤时,过滤器参数可以是一个变量。
    • 是的,我知道我在过滤什么。就像我在帖子中提到的那样,我将只处理 3 个参数:itemStatusitemCategoryitemType。只有价值观会改变。关于您的最后一条评论,如果我的 itemType 数组中传递了多个值怎么办?例如itemType["COMMON", "SALE", "EXCLUSIVE"]。那么这个catalogues.filter(prod =&gt; prod.itemStatus === 'AVAILABLE' &amp;&amp; prod.itemType === 'COMMON') 表达式将如何变化呢?
    • 除非您只过滤每个itemStatusitemCategoryitemType 一次。然后您可以使用以下catalogues.filter(prod =&gt; prod.itemStatus === itemStatus &amp;&amp; prod.itemType === itemType &amp;&amp; prod.itemCategory === itemCategory)。但是您可能想要过滤两种itemType,因此您需要找到一种方法将prod.itemType === itemType 附加到您的验证中。
    • 我提供的答案并不完整,无法直接修复您的代码,因为您必须满足用户对itemStatusitemCategoryitemType 进行联合国过滤的情况。删除过滤器后,您应该使用catalogues 再次进行过滤,因为这是任何过滤之前的真实来源。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-05
    • 1970-01-01
    • 1970-01-01
    • 2019-11-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多