【问题标题】:Filter an array based on dynamic keys in an array of nested objects根据嵌套对象数组中的动态键过滤数组
【发布时间】:2019-07-09 20:11:04
【问题描述】:

我有一个包含嵌套对象的数组。像这样的:

const results = [
    { 
        general: {
            orderID: '5567',
            created: 1548765626101,
            status: 'new'
        },

        company: {
            companyName: 'company x',
            companyEmail: 'info@companyx.com',
            companyContact: 'John Doe'
        },

        customer: {
            customerName: 'Jane Doe',
            customerEmail: 'janedoe@email.com'
        },

        products: [
            {
                productID: 4765756,
                productName: 'Product x',
                productDescription: 'Description for product x'
            },
            {
                productID: 4767839,
                productName: 'Product y',
                productDescription: 'Description for product y'
            }
        ],

        payment: {
            price: 1000,
            method: 'cash'
        }

    },
]

(为了让它有点结构化,我只为这个问题插入了一个结果对象。但假设结果数组中有 100 个元素。)

用户可以输入搜索词并选中/取消选中包含或排除这些键的键。键被硬编码在一个列表中。

例如。用户键入“jane”并检查 customerName 和 customerEmail 作为想要搜索的键。或者用户输入“x”并检查产品名称。

如何动态搜索这些选中的键?我已经在数组中有选定的键。

所以对于第一个例子,我有['customerName', 'customerEmail']

第二个是['productName']

我之前使用过array.filter() 硬编码键,但我不知道如何过滤这些动态键。

有人可以帮我分解不同的步骤吗?我正在使用 es6,没有外部库。

【问题讨论】:

  • 你想从搜索中返回什么?匹配的整个顶级对象?
  • @GabrielePetrioli 是的,一个包含所有匹配(顶级)对象的数组

标签: javascript arrays object filter ecmascript-6


【解决方案1】:

也许是这样的?请记住,“searchTerm”是类型敏感的。

用法:搜索(结果,['companyName','productName'],'x');

/**
 *  Returns an array of objects which contains at least one 'searchKey' whose value
 *  matches THE 'searchTerm'.
 */
function search( inp, searchKeys, searchTerm ) {
  let retArray = [];

  function rdp( inp, searchKeys, searchTerm ) {

    if ( Array.isArray(inp) ) {
      if (inp.length > 0) {
        inp.forEach(elem => {
            rdp( elem, searchKeys, searchTerm );
        });
      }
    }
    else {
      Object.keys( inp ).forEach( prop => {
          if ( Array.isArray( inp[ prop ] ) || ( typeof inp[ prop ] == 'object')) {
            rdp( inp[ prop ], searchKeys, searchTerm );
          }
          else {
            searchKeys.forEach( key => {
                if (( prop == key ) &&  //  key match
                    ( prop in inp)) {  //  search term found

                  switch ( typeof inp[prop] ) {
                    case 'string' : if (inp[ prop ].indexOf( searchTerm ) > -1) { retArray.push( inp ); } break;
                    case 'number' : if ( inp[ prop ] === searchTerm ) { retArray.push( inp ); } break;
                  }
                }
            });
          }
      });
    }
  }

  rdp( inp, searchKeys, searchTerm );

  return retArray;

}

【讨论】:

    【解决方案2】:

    您需要遍历results 数组,然后深入搜索每个对象以查找匹配项。为此,您需要

    • 获取所有键/值对
    • 如果值是对象,则更深入地搜索
    • 如果值是数组,则更深入地搜索每个项目
    • 否则(值为字符串或数字)
      • 如果键在要搜索的字段列表中
      • 如果值与查询匹配,则返回 true
      • 否则返回false

    类似的东西

    const deepSearcher = (fields, query) =>
      function matcher(object) {
        const keys = Object.keys(object);
    
        return keys.some(key => {
          const value = object[key];
          // handle sub arrays
          if (Array.isArray(value)) return value.some(matcher);
          // handle sub objects
          if (value instanceof Object) return matcher(value);
          // handle testable values
          if (fields.includes(key)) {
            // handle strings
            if (typeof value === "string") return value.includes(query);
            // handle numbers
            return value.toString() === query.toString();
          }
          return false;
        });
      };
    

    此函数创建一个匹配器,用于.filter 方法。

    const customerFilter = deepSearcher(['customerName', 'customerEmail'], 'jane')
    const found = results.filter(customerFilter);
    

    或者您可以直接将其传递给.filter

    const found = results.filter(deepSearcher(['customerName', 'customerEmail'], 'jane'));
    

    您传递给 deepSearcher 的字段不必属于同一个对象。匹配器将测试任何匹配项(但它们必须指向字符串/数字才能使此代码正常工作)。


    工作测试用例

    const results = [{
      general: {
        orderID: "5567",
        created: 1548765626101,
        status: "new"
      },
      company: {
        companyName: "company x",
        companyEmail: "info@companyx.com",
        companyContact: "John Doe"
      },
      customer: {
        customerName: "Jane Doe",
        customerEmail: "janedoe@email.com"
      },
      products: [{
          productID: 4765756,
          productName: "Product x",
          productDescription: "Description for product x"
        },
        {
          productID: 4767839,
          productName: "Product y",
          productDescription: "Description for product y"
        }
      ],
      payment: {
        price: 1000,
        method: "cash"
      }
    }];
    
    const deepSearcher = (fields, query) =>
      function matcher(object) {
        const keys = Object.keys(object);
    
        return keys.some(key => {
          const value = object[key];
          // handle sub arrays
          if (Array.isArray(value)) return value.some(matcher);
          // handle sub objects
          if (value instanceof Object) return matcher(value);
          // handle testable values
          if (fields.includes(key)) {
            // handle strings
            if (typeof value === "string") return value.includes(query);
            // handle numbers
            return value.toString() === query.toString();
          }
          return false;
        });
      };
    
    const matchingCustomer = results.filter(deepSearcher(["customerName", "customerEmail"], 'jane'));
    console.log('results with matching customer:', matchingCustomer.length);
    
    const matchingProduct = results.filter(deepSearcher(["productName"], 'x'));
    console.log('results with matching product:', matchingProduct.length);
    
    
    const matchingPrice = results.filter(deepSearcher(["price"], '1000'));
    console.log('results with matching price:', matchingPrice.length);
    
    const nonMatchingPrice = results.filter(deepSearcher(["price"], '500'));
    console.log('results with non matching price:', nonMatchingPrice.length);

    【讨论】:

      猜你喜欢
      • 2020-03-11
      • 2020-05-22
      • 1970-01-01
      • 1970-01-01
      • 2022-11-30
      • 2018-11-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多