首页 > 解决方案 > React中具有多个值的多个列表框搜索数组过滤器

问题描述

我正在从 O365 REST 调用中填充一个对象,然后使用 SPfx FluentUI 详细信息列表应用程序中的多选下拉菜单进行过滤。我的代码是附加来自每个下拉列表的任何值的结果,而不是缩小结果以满足所有过滤条件。

示例 Codepen:https ://codepen.io/detailcode/pen/VwmdZoo

<!DOCTYPE html><html><body>
<select id="lstTitle" multiple size="4">
   <option value="" selected>(Select Multi Values)</option>
   <option value="Apple">Apple</option>
   <option value="Pear">Pear</option>
   <option value="Grape">Grape</option>
   <option value="Banana">Banana</option>
   <option value="Beet">Beet</option>
   <option value="Grapefruit">Grapefruit</option>
   <option value="Lemon">Lemon</option>
</select>
<select id="lstColor" multiple size="4">
   <option value="" selected>(Select Multi Values)</option>
   <option value="Red">Red</option>
   <option value="Green">Green</option>
   <option value="Yellow">Yellow</option>
</select>
<select id="lstTaste" multiple size="4">
   <option value="" selected>(Select Multi Values)</option>
   <option value="Sweet">Sweet</option>
   <option value="Sour">Sour</option>
   <option value="Bitter">Bitter</option>
</select>

<input id="butFilter" type="button" value="Filter" onclick="butFilter_Click()" />

<p>All Results:</p>
<div id="cssResults1" style="padding:3px 10px;border:1px solid #000;">No data</div>
<p>Single Select Result:</p>
<div id="cssResults2" style="padding:3px 10px;border:1px solid #000;">No data</div>
<p>Multi Select Result: (Selecting Apple, Pear, Grape, Red, Green, Sweet, Sour) <strong>Desired result being records 1, 2, 4, 5, 6</strong></p>
<div id="cssResults3" style="padding:3px 10px;border:1px solid #000;">No data</div>

<script>
let data = [
    { 'id': 1, 'title': 'Apple', 'color': 'Red', 'taste': 'Sweet', },
    { 'id': 2, 'title': 'Apple', 'color': 'Green', 'taste': 'Sour', },
    { 'id': 3, 'title': 'Apple', 'color': 'Yellow', 'taste': 'Sweet', },
    { 'id': 4, 'title': 'Pear', 'color': 'Green', 'taste': 'Sweet', },
    { 'id': 5, 'title': 'Grape', 'color': 'Red', 'taste': 'Sweet', },
    { 'id': 6, 'title': 'Grape', 'color': 'Green', 'taste': 'Sour', },
    { 'id': 7, 'title': 'Banana', 'color': 'Yellow', 'taste': 'Sweet', },
    { 'id': 8, 'title': 'Beet', 'color': 'Red', 'taste': 'Bitter', },
    { 'id': 9, 'title': 'Grapefruit', 'color': 'Yellow', 'taste': 'Bitter', },
    { 'id': 10, 'title': 'Lemon', 'color': 'Yellow', 'taste': 'Sour', },
]

//var result1 = data.filter(e => e.color == 'Yellow' && e.taste == 'Sweet');
console.log(data);
document.getElementById("cssResults1").innerHTML = "<p>" + JSON.stringify(data) + "<p>";

function butFilter_Click() { 
  var result2 = data.filter(e => 
  e.title == (document.getElementById("lstTitle").value != "" ? document.getElementById("lstTitle").value : e.title) && 
  e.color == (document.getElementById("lstColor").value != "" ? document.getElementById("lstColor").value : e.color) && 
  e.taste == (document.getElementById("lstTaste").value != "" ? document.getElementById("lstTaste").value : e.taste));
  document.getElementById("cssResults2").innerHTML = "<p>" + JSON.stringify(result2) + "<p>";
  console.log(result2); // LOG SINGLE SELECT RESULT
  
  const valTitle = Array.apply(null, document.getElementById("lstTitle").options).filter(option => option.selected).map(option => option.value);
  const valColor = Array.apply(null, document.getElementById("lstColor").options).filter(option => option.selected).map(option => option.value);
  const valTaste = Array.apply(null, document.getElementById("lstTaste").options).filter(option => option.selected).map(option => option.value);
  console.log(valTitle); // LOG lstTitle MULTI VALUES
  console.log(valColor); // LOG lstColor MULTI VALUES
  console.log(valTaste); // LOG lstTaste MULTI VALUES
  
  // INCLUSIVELY ADDS TO RESULT VERSUS MATCHING ALL KEY VALUES
  let tempContat = [];
  let temp;
  for(var i = 0; i < valTitle.length; i++) {
    temp = data.filter(e => e.title == (valTitle[i] != "" ? valTitle[i] : e.title))
    tempContat = tempContat.concat(temp);
  }
  for(var i = 0; i < valColor.length; i++) {
    temp = data.filter(e => e.color == (valColor[i] != "" ? valColor[i] : e.color))
    tempContat = tempContat.concat(temp);
  }
  for(var i = 0; i < valTaste.length; i++) {
    temp = data.filter(e => e.taste == (valTaste[i] != "" ? valTaste[i] : e.taste))
    tempContat = tempContat.concat(temp);
  }
  
  var result3 = tempContat;
  document.getElementById("cssResults3").innerHTML = "<p>" + JSON.stringify(result3) + "<p>";
  console.log(result3); // LOG MULTI SELECT RESULT
}
</script>
</body></html>

更新:如何过滤对象以在最终结果中包含(与结果3相反)?选择水果(苹果、梨、葡萄)、颜色(红色、绿色)和口味(甜、酸)时,预期结果应为 ID 1、2、4、5 和 6。如果每个搜索条件不符合,它应该像在搜索引擎过滤器中一样被排除。

标签: javascriptarraysreactjsfilterfluentui-react

解决方案


问题是您拥有的每个 for 循环都独立于用于过滤掉对象的谓词。要解决此问题,您需要将所有谓词置于同一范围内,以便由于当前谓词的范围而不会添加应该省略的内容。例如:

  for(var i = 0; i < valTitle.length; i++) {
    // this predicate will remove all things relating to titles
    temp = data.filter(e => e.title == (valTitle[i] != "" ? valTitle[i] : e.title))
    tempContat = tempContat.concat(temp);
  }
  // these for loops are independent and all re-use the same 'data' so its possible for the previous loop's omitted objects to be filtered in again
  // because the filter constraint in the next for loop doesn't include the previous ones constraints
  for(var i = 0; i < valColor.length; i++) {
    // this filter's predicate is unaware that 'e.title' of some type should be omitted, it only knows to filter out colors of some type `valColor[i]`
    temp = data.filter(e => e.color == (valColor[i] != "" ? valColor[i] : e.color))
    // here some titles that should be omitted are added in because it matched on the color from the predicate above
    tempContat = tempContat.concat(temp);
  }
  for(var i = 0; i < valTaste.length; i++) {
    temp = data.filter(e => e.taste == (valTaste[i] != "" ? valTaste[i] : e.taste))
    tempContat = tempContat.concat(temp);
  }
  

正因为如此,它也可以添加重复项,因为过滤器是基于data永远不会更改的,并且是要过滤的所有可用内容。

要解决此问题,您需要组合谓词,下面我通过使用 1 个过滤器并将谓词放在一起来做到这一点。

  const result3 = data.filter((e) => {
    // element must have at least one of these titles to be filtered in
    if (!valTitle.some((title) => e.title == (title != "" ? title : e.title))) {
      return false;
    }
    // and
    
    // element must have at least one of these colors to be filtered in
    if (!valColor.some((color) => e.color == (color != "" ? color : e.color))) {
      return false;
    }
    // and
    
    // element must have at least one of these tastes to be filtered in
    if (!valTaste.some((taste) => e.taste == (taste != "" ? taste : e.taste))) {
      return false;
    }
    
    return true;
  });

为此的代码笔

您可以执行与上述解决方案类似的操作。


推荐阅读