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

JS/TS根据单个过滤对象属性值过滤对象数组

问题背景

我需要对API调用返回的结果集进行过滤,该结果集为强类型对象构成的数组,下文称其为resultsArray。

过滤所用的条件来自另一个对象,下文称其为filterObject,该对象的属性名与resultsArray中对象的属性名完全一致。过滤逻辑为:遍历resultsArray时,若数组项在两个对象共有的属性上的值与filterObject对应属性值完全匹配,则保留该数组项。

实际业务中,过滤条件来自UI表单,规则如下:

  • 若filterObject中未设置任何有效条件(所有属性值为null),则返回并展示全部resultsArray数据
  • 若设置了部分条件,则仅展示匹配所有已设置条件的记录

例如下方的filterObject:

const filterObject = {
  usdotnumber: '13',
  mcnumber: null,
  mxnumber: null,
  ffnumber: null,
  vatnumber: '12345',
  nvocc: null,
};

上述示例中,仅需要保留usdotnumber = 13 且 vatnumber = 12345的记录。

遇到的问题

尝试过多种公开方案,但大多是对象与对象、数组与数组的比对,没有找到单个对象与对象数组做过滤匹配的合适方案,实现过程中遇到了「key不可索引」的报错:因为filterObject是单对象而非可索引的数组/类数组结构,无法直接用字符串键索引取值。

补充说明1

具体报错为字符串无法作为类型索引,报错信息如下:

Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'filters'. No index signature with a parameter of type 'string' was found on type 'filters'.

触发报错的代码片段如下:

processData(carriers: MultiCarrierResponse): void {
  interface filters {
    ffnumber: string | null;
    mcnumber: string | null;
    mxnumber: string | null;
    nvocc: string | null;
    usdotnumber: string | null;
    vatnumber: string | null;
  }

  const filterObject: filters = {
    usdotnumber: '13',
    mcnumber: null,
    mxnumber: null,
    ffnumber: null,
    vatnumber: '12345',
    nvocc: null,
  };

  const filteredResults = carriers?.carriers?.filter((o) =>
    Object.keys(filterObject).every((k) => filterObject[k] === o[k]),
  );

  console.log('The filtered results: ', filteredResults);

  carriers?.carriers?.forEach((element) => {
    this.values.push({
      carrierId: element.carrierId,
      carrierName: element.carrierName,
      carrierStatus: element.carrierStatus,
    });
  });

  console.log('The carriers: ', carriers);
}

补充说明2

调整为如下代码后解决了TS类型报错:

filterByObject(values: any[] | null | undefined, filterObject: any): any {
  const filterKeys = Object.keys(filterObject).filter(
    (key) => filterObject[key] !== null,
  );

  return values?.filter((value: { [x: string]: any }) => {
    return filterKeys.every((key) => filterObject[key] === value[key]);
  });
}

该代码可通过TS类型校验,但仍存在逻辑问题:无法正确处理resultsArray中对应filterObjectnull值属性的匹配逻辑,需要完善实现方案。

实现方案

核心修复点

  1. 提前提取filterObject中值不为null的有效键值对,null代表不设置该字段的过滤条件,不参与匹配逻辑
  2. 用TS泛型做类型约束,不使用any绕开类型检查,从根源解决字符串索引报错,同时保留完整类型提示
  3. 增加边界判断:如果没有有效过滤条件,直接返回全量原数组,减少不必要的遍历
  4. 修复原代码逻辑bug:过滤完成后不要遍历原数组处理数据,直接使用过滤后的结果

通用过滤函数实现

/**
 * 按单对象条件过滤对象数组
 * @param values 待过滤的对象数组
 * @param filterObject 过滤条件对象,值为null的属性不参与过滤
 * @returns 过滤后的数组
 */
function filterByObject<T extends Record<string, any>>(
  values: T[] | null | undefined,
  filterObject: Partial<Record<keyof T, T[keyof T] | null>>
): T[] {
  // 源数组为空时直接返回空数组
  if (!values) return [];

  // 提取所有非null的有效过滤条件
  const validFilterEntries = Object.entries(filterObject).filter(
    ([, value]) => value !== null
  ) as [keyof T, T[keyof T]][];

  // 无有效过滤条件,返回全量数据
  if (validFilterEntries.length === 0) {
    return [...values];
  }

  // 匹配所有有效条件的项才保留
  return values.filter((item) => {
    return validFilterEntries.every(([key, targetValue]) => item[key] === targetValue);
  });
}

业务场景调用示例

对应原processData方法的改造:

processData(carriers: MultiCarrierResponse): void {
  // 对齐接口返回结构定义数据类型
  interface Carrier {
    ffnumber: string | null;
    mcnumber: string | null;
    mxnumber: string | null;
    nvocc: string | null;
    usdotnumber: string | null;
    vatnumber: string | null;
    carrierId: string | number;
    carrierName: string;
    carrierStatus: string | number;
  }

  const filterObject: Partial<Record<keyof Carrier, Carrier[keyof Carrier] | null>> = {
    usdotnumber: '13',
    mcnumber: null,
    mxnumber: null,
    ffnumber: null,
    vatnumber: '12345',
    nvocc: null,
  };

  // 调用通用过滤方法拿到结果
  const filteredResults = filterByObject<Carrier>(carriers?.carriers, filterObject);
  console.log('The filtered results: ', filteredResults);

  // 基于过滤后的结果做后续处理
  this.values = filteredResults.map((element) => ({
    carrierId: element.carrierId,
    carrierName: element.carrierName,
    carrierStatus: element.carrierStatus,
  }));

  console.log('The carriers: ', carriers);
}

原代码问题说明

  • 最早的TS索引报错:Object.keys()默认返回string[]类型,TS不允许用宽泛的string类型索引没有定义字符串索引签名的自定义接口,通过泛型约束+类型收窄即可解决,不需要用any丢失类型安全
  • 之前调整的版本问题:没有处理「无有效过滤条件返回全量」的边界,且全量使用any丢失了类型提示,容易写出隐藏bug
  • 原业务代码逻辑bug:计算出filteredResults后仍然遍历原全量数组做数据转换,导致过滤逻辑完全不生效,改造后直接基于过滤结果做转换即可。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 12:21:34