You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript中过滤API返回的JSON数组数据?

解决方案

首先,你的API返回数据是嵌套结构(数组包裹分类对象,分类对象的键为分类名,值为对应页面数组),这种结构不利于直接过滤。我们先将数据扁平化,再实现支持多条件的过滤逻辑,同时结合TypeScript保证类型安全。

1. 定义TypeScript类型

先明确数据结构的类型,避免类型错误:

// 单个页面的基础类型
interface Page {
  title: string;
  showTitle: string;
  description: string;
  page: string;
  tags: string[];
}

// 扁平化后的条目,新增分类信息字段
interface PageWithCategory extends Page {
  category: string;
}

2. 扁平化原始数据

将嵌套的分类-页面结构转换成带分类信息的平级数组,简化后续过滤逻辑:

const flattenPages = (rawData: Array<Record<string, Page[]>>): PageWithCategory[] => {
  return rawData.flatMap(categoryGroup => 
    Object.entries(categoryGroup).flatMap(([category, pages]) => 
      pages.map(page => ({ ...page, category }))
    )
  );
};

3. 实现多条件过滤函数

编写支持按标题、描述关键词、标签、分类过滤的函数,可组合多个条件:

const filterPages = (
  pages: PageWithCategory[],
  filters: {
    searchQuery?: string;
    selectedTags?: string[];
    selectedCategory?: string;
  }
): PageWithCategory[] => {
  return pages.filter(page => {
    // 匹配标题/描述的关键词(不区分大小写)
    const matchesSearch = !filters.searchQuery || 
      page.title.toLowerCase().includes(filters.searchQuery.toLowerCase()) ||
      page.description.toLowerCase().includes(filters.searchQuery.toLowerCase());

    // 匹配标签:支持任意标签匹配(若需全匹配可改成every)
    const matchesTags = !filters.selectedTags || 
      page.tags.some(tag => filters.selectedTags!.includes(tag));

    // 匹配分类
    const matchesCategory = !filters.selectedCategory || 
      page.category === filters.selectedCategory;

    // 所有条件满足才保留条目
    return matchesSearch && matchesTags && matchesCategory;
  });
};

4. 在React组件中结合状态使用

结合React状态管理搜索关键词、选中的标签/分类,实时过滤数据:

import { useState, useEffect, useMemo } from 'react';

// 复用前面的类型定义
interface Page { /* ... */ }
interface PageWithCategory extends Page { /* ... */ }

const DocsFilter = () => {
  const [jsonData, setJsonData] = useState<Array<Record<string, Page[]>>>([]);
  const [searchQuery, setSearchQuery] = useState<string>("");
  const [selectedTags, setSelectedTags] = useState<string[]>([]);
  const [selectedCategory, setSelectedCategory] = useState<string>("");

  // 从API获取数据(复用你的原代码)
  useEffect(() => {
    fetch(`http://127.0.0.1:8000/api/docs`, {
      method: "GET",
      headers: { 'Accept': 'application/json' }
    })
    .then(response => {
      if (response.status === 200) {
        return response.json();
      } throw new Error("Documentation data cannot be located.")
    })
    .then(data => setJsonData(data))
    .catch(err => console.error(err));
  }, []);

  // 缓存扁平化后的数据,避免重复计算
  const flattenedPages = useMemo(() => flattenPages(jsonData), [jsonData]);

  // 缓存过滤后的结果,提升性能
  const filteredPages = useMemo(() => 
    filterPages(flattenedPages, { searchQuery, selectedTags, selectedCategory }),
    [flattenedPages, searchQuery, selectedTags, selectedCategory]
  );

  return (
    <div>
      {/* 搜索输入框 */}
      <input
        type="text"
        value={searchQuery}
        onChange={(e) => setSearchQuery(e.target.value)}
        placeholder="搜索标题/描述关键词..."
      />

      {/* 分类选择器 */}
      <select
        value={selectedCategory}
        onChange={(e) => setSelectedCategory(e.target.value)}
      >
        <option value="">所有分类</option>
        {Array.from(new Set(flattenedPages.map(p => p.category))).map(category => (
          <option key={category} value={category}>{category}</option>
        ))}
      </select>

      {/* 标签复选框组 */}
      <div>
        {Array.from(new Set(flattenedPages.flatMap(p => p.tags))).map(tag => (
          <label key={tag}>
            <input
              type="checkbox"
              checked={selectedTags.includes(tag)}
              onChange={(e) => {
                setSelectedTags(prev => 
                  e.target.checked ? [...prev, tag] : prev.filter(t => t !== tag)
                );
              }}
            />
            {tag}
          </label>
        ))}
      </div>

      {/* 渲染过滤结果 */}
      <div>
        {filteredPages.length === 0 ? (
          <p>没有找到匹配的内容</p>
        ) : (
          <ul>
            {filteredPages.map((page, idx) => (
              <li key={idx}>
                <h3>{page.showTitle} <small>({page.category})</small></h3>
                <p>{page.description}</p>
                <div>标签:{page.tags.join(", ")}</div>
                <a href={`/${page.page}`} target="_blank" rel="noopener noreferrer">查看文档</a>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
};

export default DocsFilter;

关键说明

  • 扁平化数据:将原嵌套结构转换为平级数组是简化过滤逻辑的核心,让每个页面条目都携带分类信息,无需再处理嵌套遍历。
  • 类型安全:TypeScript类型定义能提前发现数据结构相关错误,避免运行时问题。
  • 性能优化:使用useMemo缓存扁平化和过滤结果,防止每次渲染重复执行计算密集的操作。
  • 规则灵活调整:过滤函数中的条件可按需修改,比如标签改成全匹配、搜索区分大小写等。

内容的提问来源于stack exchange,提问作者Owen Boreham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 11:53:15