0

我有一组 AppItems。每个应用程序项都有一个属性,该属性是 ProfileItems 数组。我想根据哪个 AppItem 的名称包含我的搜索文本的配置文件来过滤我的 AppItems 数组。它应该在包含搜索文本的第一个配置文件上返回 True。

问题是我在过滤器函数的 foreach 中循环遍历配置文件项,我认为它不喜欢。我不知道该怎么做。

export interface AppState {
  appItems: AppItem[];
}

export interface AppItem {
  profiles: ProfileItem[];
  ...
}

export interface ProfileItem {
  name: string;
  ...
}

appItemsFiltered(state) {
    return state.appItems
    .filter((item: AppItem) => {
      if (!state.filters.searchQuery) return true;

      item.profiles.forEach(function (profile, index) {
        const name = profile.name.toLowerCase()
        const text = state.filters.searchQuery?.toLowerCase();
        const result = name.indexOf(text)
        if (result !== -1) return true;
      })
      return false
    };
  }
4

3 回答 3

2

如果数组是:

const array = [
  {profiles: [{name: 'name 10'}, {name: 'name 11'}]},
  {profiles: [{name: 'name 20'}, {name: 'name 21'}]},
  // ...
];

像这样过滤:

const filterText = 'name 21';
const result = array.filter(x => x.profiles.some(x => x.name === filterText));

result将是一个匹配数组。

const hasFound = result.length > 0;
于 2021-06-07T17:02:43.807 回答
0

当您return在 a 中时,它与在循环forEach中使用相同;它只是继续循环中的下一个迭代。continuefor

如果您正在寻找“至少一个结果是true”,那么请考虑使用Array.prototype.some,它会根据数组中的单个结果是否解析为true.

这是我尝试使用该解决方案重写代码的尝试,尽管使用提供的数据我不能做得比这更好:

appItemsFiltered(state) {
    return state.appItems
    .filter((item: AppItem) => {
      if (!state.filters.searchQuery) return true;

      return item.profiles.some(function (profile, index) {
        const name = profile.name.toLowerCase()
        const text = state.filters.searchQuery?.toLowerCase();
        const result = name.indexOf(text)
        if (result !== -1) return true;
      })
    };
  }
于 2021-06-07T16:58:45.973 回答
0

我写了一个片段,介绍了两种方法:

  • join()字符串 & 在结果字符串中搜索
  • some()与_find()

const AppState = {
  appItems: [{
      profileItem: ['1.1', '1.2', '1.3'],
    },
    {
      profileItem: ['2.1', '2.2', '2.3'],
    },
    {
      profileItem: ['3.1', '3.2', '3.3'],
    }
  ]
}

// creating an HTML representation of the list
const itemHtml = (item) => {
  return `<li>${ item }</li>`
}

const profileItemsHtml = (profileItems) => {
  return profileItems.map(item => itemHtml(item)).join('')
}

const createList = (appItems) => {
  return appItems.reduce((a, {
    profileItem
  }) => {
    a = [...a, ...profileItem]
    return a
  }, [])
}

// putting out the HTML
const updateListContainer = (container, list) => {
  container.innerHTML = profileItemsHtml(list)
}

// the full list
const fullList = createList(AppState.appItems)
const fullListContainer = document.getElementById('full-list')
updateListContainer(fullListContainer, fullList)

// initiating the filtered list
let filteredList1 = fullList
const filteredListContainer1 = document.getElementById('filtered-list-1')
updateListContainer(filteredListContainer1, filteredList1)

let filteredList2 = fullList
const filteredListContainer2 = document.getElementById('filtered-list-2')
updateListContainer(filteredListContainer2, filteredList2)

// setting up filtering on input field input event
const filterInput = document.getElementById('filter-input')

filterInput.addEventListener('input', function(e) {
  // FILTER 1: use join()
  // if the list is made up of only strings, then you
  // could join them & search the whole string at once
  // might yield errors in edge cases, but mostly it
  // should be correct, I think
  // edge case example: 22 (you can try it)
  const filtered1 = AppState.appItems.find(({
    profileItem
  }) => profileItem.join('').includes(e.target.value))
  if (filtered1 && e.target.value) {
    updateListContainer(filteredListContainer1, filtered1.profileItem)
  } else {
    updateListContainer(filteredListContainer1, fullList)
  }

  // FILTER 2: use SOME
  const filtered2 = AppState.appItems.find(({
    profileItem
  }) => profileItem.some(item => item.includes(e.target.value)))
  if (filtered2 && e.target.value) {
    updateListContainer(filteredListContainer2, filtered2.profileItem)
  } else {
    updateListContainer(filteredListContainer2, fullList)
  }
})
.list-container {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
<div>
  <label for="filter-input">
    Filter:
    <input type="text" id="filter-input" />
  </label>
  <hr>
  <div class="list-container">
    <div>
      FULL LIST:
      <ul id="full-list"></ul>
    </div>
    <div>
      FILTERED WITH JOIN:
      <ul id="filtered-list-1"></ul>
    </div>
    <div>
      FILTERED WITH SOME:
      <ul id="filtered-list-2"></ul>
    </div>
  </div>
</div>

于 2021-06-07T17:32:10.123 回答