【问题标题】:How to optimize search algorithm如何优化搜索算法
【发布时间】:2019-10-17 17:51:11
【问题描述】:

我在一行中有 4 个下拉多选过滤器。

需要在每个选定的选项上渲染和。 我需要在新数组中添加该选定选项并更新当前数组 -> object.property.selected = false/true。

我正在搜索对象数组,每个对象的一个​​属性都有对象数组。

您可以在此代码笔上找到代码示例:https://codepen.io/nikolatrajkovicits/pen/JqpWOX?editors=0012

这是该数组的外观:

export const getFiltersList = () => [
  {
    title: 'Taxonomy',
    options: [
      {
        id: 0,
        name: 'Competitions',
        selected: false,
        key: 'Taxonomy'
      },
      {
        id: 1,
        name: 'Events',
        selected: false,
        key: 'Taxonomy'
      },
      {
        id: 2,
        name: 'Data',
        selected: false,
        key: 'Taxonomy'
      },
      {
        id: 3,
        name: 'Organisations',
        selected: false,
        key: 'Taxonomy'
      },
      {
        id: 4,
        name: 'Players',
        selected: false,
        key: 'Taxonomy'
      },
      {
        id: 5,
        name: 'Teams',
        selected: false,
        key: 'Taxonomy'
      }
    ]
  },
  {
    title: 'Source',
    options: [
      {
        id: 0,
        name: 'Facebook',
        selected: false,
        key: 'Source'
      },
      {
        id: 1,
        name: 'Twitter',
        selected: false,
        key: 'Source'
      },
      {
        id: 2,
        name: 'Instagram',
        selected: false,
        key: 'Source'
      },
      {
        id: 3,
        name: 'Websites',
        selected: false,
        key: 'Source'
      }
    ]
  },
  {
    title: 'Timeframe',
    options: [
      {
        id: 0,
        name: 'Past hour',
        selected: false,
        key: 'Timeframe'
      },
      {
        id: 1,
        name: 'Past 24 hours',
        selected: false,
        key: 'Timeframe'
      },
      {
        id: 2,
        name: 'Past week',
        selected: false,
        key: 'Timeframe'
      },
      {
        id: 3,
        name: 'Past month',
        selected: false,
        key: 'Timeframe'
      },
      {
        id: 4,
        name: 'Past year',
        selected: false,
        key: 'Timeframe'
      }
    ]
  },
  {
    title: 'Location',
    options: [
      {
        id: 0,
        name: 'Location 1',
        selected: false,
        key: 'Location'
      },
      {
        id: 1,
        name: 'Location 2',
        selected: false,
        key: 'Location'
      },
      {
        id: 2,
        name: 'Location 3',
        selected: false,
        key: 'Location'
      },
      {
        id: 3,
        name: 'Location 4',
        selected: false,
        key: 'Location'
      }
    ]
  }
];

这里是算法:

selectFilter = ({ id, key }) => {
    const { filtersList, selectedFilters } = this.state;
    const tempFilters = filtersList;
    const tempSelected = selectedFilters;
    const i = tempSelected.length;

    tempFilters.forEach((filter) => {
      if (filter.title === key) {
        filter.options.forEach((option) => {
          if (option.id === id) {
            option.selected = !option.selected;
            const isFilterExist = tempSelected.filter(
              (selectedFilter) => selectedFilter.name === option.name
            );
            if (!isFilterExist.length) {
              const selectedItem = { name: option.name, key, id };
              tempSelected[i] = selectedItem;
            }
          }
        });
      }
    });
    this.setState({
      filtersList: tempFilters,
      selectedFilters: tempSelected
    });
  };

在代码笔上,您可以找到纯 javascript 代码的版本。

如何让搜索算法更干净更快速?

任何建议文章、教程、评论?

【问题讨论】:

  • 不确定我是否同意您尝试管理选定的方式,我在下面有解决方案。

标签: javascript arrays reactjs algorithm search


【解决方案1】:

首先这两个是不必要的:

const tempFilters = filtersList;
const tempSelected = selectedFilters;

变量are references to the exact same objects 并且您已经有了这些变量。您可以简单地使用filtersListselectedFilters 分别代替tempFilterstempSelected


tempFilters.forEach((filter) => {
      if (filter.title === key) {`

由于.forEach 的全部内容是if,因此这表明您需要改用.filter

tempFilters.filter((filter) => filter.title === key)`

这里也一样,当你进入if

filter.options.forEach((option) => {
  if (option.id === id) {

你可以简化为

filter.options.filter((option) => option.id === id)

继续,这条线是浪费

const isFilterExist = tempSelected.filter(
    (selectedFilter) => selectedFilter.name === option.name
);

无需在整个数组上运行.filter,您只关心是否有任何项目与谓词匹配。使用.some 直接给你一个布尔值:

const isFilterExist = tempSelected.some(
    (selectedFilter) => selectedFilter.name === option.name
);

此外,由于您总是反转布尔值,因此找到相反的值会更简单一些 - 如果过滤器 存在 - 反转谓词并使用 .every 来得到它。原因是 some 的元素匹配,但你想要相反的,这在逻辑上是 every 的元素匹配谓词的完全相反。最后,我更改了名称以使用has 听起来更自然一些。

const hasNoFilter = tempSelected.every(
    (selectedFilter) => selectedFilter.name !== option.name
);

所以,作为开始,代码可以重写为:

selectFilter = ({ id, key }) => {
  const { filtersList, selectedFilters } = this.state;
  const i = selectedFilters.length;

  filtersList
    .filter((filter) => filter.title === key)
    .forEach(filter => {
      filter.options
        .filter((option) => option.id === id)
        .forEach(option => {
          option.selected = !option.selected;
          const hasNoFilter = tempSelected.every(
            (selectedFilter) => selectedFilter.name !== option.name
          );

          if (hasNoFilter) {
            const selectedItem = { name: option.name, key, id };
            selectedFilters[i] = selectedItem;
          }
        });
    });
  this.setState({
    filtersList,
    selectedFilters
  });
};

这使得推理代码更容易一些。


您似乎有具有唯一标题的过滤器和其中具有唯一 ID 的选项,这意味着标题 + ID 的组合也是唯一的。

考虑到这一点,您对单个值感兴趣,而不是任何数量的值,因此您不需要执行所有循环。您可以使用.find 获取您感兴趣的值,从而使整个代码更易于阅读:

selectFilter = ({ id, key }) => {
    const { filtersList, selectedFilters } = this.state;

    const filter = filtersList.find((filter) => filter.title === key);

    const option = filter.options.find((option) => option.id === id)

    option.selected = !option.selected;
    const hasNoFilter = tempSelected.every(
      (selectedFilter) => selectedFilter.name !== option.name
    );
    if (hasNoFilter) {
      const selectedItem = { name: option.name, key, id };
      selectedFilters.push(selectedItem);
    }

    this.setState({
      filtersList,
      selectedFilters
    });
  };
}

由于此代码仅适用于单个项目,因此您不需要该行 const i = selectedFilters.length; 因为您将在数组中进行一次插入。以前,包含在所有循环中的行 selectedFilters[i] = selectedItem; 建议您插入多个值但都在同一位置,以便只留下最后一个。一个简单的.push 就足以追加到末尾。

【讨论】:

    【解决方案2】:

    如果我是你,我会在使用之前更改 Json 对象的结构。例如:如果 Json 成为键值对,其中键是标题,值是表示选项的数组。这将使您摆脱第一个循环,您将获得 o(1) 中的选项列表,就像哈希表的复杂性一样。 注意:我假设这里的标题是唯一的。 我要做的第二个更改:您想按 ID 搜索,并且从您提到的示例中,选项数组按 id 排序,这意味着第一个元素的 id = 0 并且它位于索引 0 等处,所以如果新表示的值是数组索引 i 处元素的访问也是 o(1) 这样您也将摆脱第二个循环。 注意:如果标题不是唯一的或选项 ID 未排序,您需要更新示例以获得更好的解决方案。

    【讨论】:

    • @Soha 很好的建议,非常感谢。我会将您的建议与 VLAZ 建议结合起来。这真的很有帮助。
    【解决方案3】:

    所以我有一个反应实现,正如你标记的那样。

    简而言之,我将这两个问题(datastate)从您的入站数据中分离出来。此外,您传入的数据还有 2 个不必要的属性(selectedkey)。

    key - 这是您在返回函数期间添加的反应实现。我使用了你的标题,因为它在每个对象中都是独一无二的。

    selected - 数据应该包含进入应用程序的重要属性,这被归类为应用程序状态而不是数据。如果您采用这种方法,您将增加更多的计算开销。当您选择一个项目时,您必须将所有其他实例更新为 false。我将此状态提升到主应用程序。

    这是我写的代码:

    dropdown-data.json:

    [
      {
        "title": "Taxonomy",
        "options": [
          {
            "id": 0,
            "name": "Competitions"
          },
          {
            "id": 1,
            "name": "Events"
          },
          {
            "id": 2,
            "name": "Data"
          },
          {
            "id": 3,
            "name": "Organisations"
          },
          {
            "id": 4,
            "name": "Players"
          },
          {
            "id": 5,
            "name": "Teams"
          }
        ]
      },
      {
        "title": "Source",
        "options": [
          {
            "id": 0,
            "name": "Facebook"
          },
          {
            "id": 1,
            "name": "Twitter"
          },
          {
            "id": 2,
            "name": "Instagram"
          },
          {
            "id": 3,
            "name": "Websites"
          }
        ]
      },
      {
        "title": "Timeframe",
        "options": [
          {
            "id": 0,
            "name": "Past hour"
          },
          {
            "id": 1,
            "name": "Past 24 hours"
          },
          {
            "id": 2,
            "name": "Past week"
          },
          {
            "id": 3,
            "name": "Past month"
          },
          {
            "id": 4,
            "name": "Past year"
          }
        ]
      },
      {
        "title": "Location",
        "options": [
          {
            "id": 0,
            "name": "Location 1"
          },
          {
            "id": 1,
            "name": "Location 2"
          },
          {
            "id": 2,
            "name": "Location 3"
          },
          {
            "id": 3,
            "name": "Location 4"
          }
        ]
      }
    ]
    

    <App />:

    import React from "react";
    
    import dropdownData from "./dropdown-data.json";
    
    const Dropdown = ({ options = [], ...props }) => (
      <select {...props}>
        {options.map(({ id, ...option }) => (
          <option key={id}>{option.name}</option>
        ))}
      </select>
    );
    
    const App = () => {
      const [data, setData] = React.useState({});
    
      return (
        <>
          {/* A debugging helper :) */}
          <pre>{JSON.stringify(data, null, 2)}</pre>
    
          {dropdownData.map(({ title, options }) => (
            <Dropdown
              key={title}
              {...{ options }}
              onChange={({ target }) =>
                setData({
                  ...data,
                  [title.toLowerCase()]: target.value
                })
              }
            />
          ))}
        </>
      );
    };
    
    export default App;
    

    pre 标签显示下面返回的数据。由于初始状态存在一个空对象,因此您的 state 不会填充未触及/不需要的数据。

    让我知道这有什么帮助,这是 create-react-app 中的 2 个文件。

    【讨论】:

    • 感谢尼尔的帮助!
    • 你觉得它有用吗?您对实施有任何疑问吗?
    • 是的,这是解决问题的全新和不同的方法,它很有启发性,我肯定会在未来使用它。到目前为止,我还没有想到这个解决方案。很有用!
    猜你喜欢
    • 2016-12-05
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    • 2012-05-24
    • 2010-10-26
    • 2012-09-05
    • 2017-04-13
    • 1970-01-01
    相关资源
    最近更新 更多