简体   繁体   English

通过多个匹配条件递归过滤无限嵌套对象数组,但仅返回具有两个匹配实例的父对象

[英]Recursively filter an array of infinitely nested objects by mutliple matching conditions but only return parent that has an instance of both matches

I have the following array of objects;我有以下对象数组; however this could be any unknown key/value and be infinitely nested, for now this is a testing sample:但是,这可能是任何未知的键/值并且可以无限嵌套,现在这是一个测试示例:

[
  {
    "reference_id": "R123",
    "customer": "Person 1",
    "customer_email": "person1@email.com",
    "location": "UK",
    "bookings": [
      {
        "product": "Product 1",
        "provider": "Company 1",
        "cancellable": true
      },
      {
        "product": "Product 2",
        "provider": "Company 2",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  },
  {
    "reference_id": "R1234",
    "customer": "Person 2",
    "customer_email": "person2@email.com",
    "location": "USA",
    "bookings": [
      {
        "product": "Product 1",
        "provider": "Company 1",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  },
  {
    "reference_id": "R12345",
    "customer": "Person 3",
    "customer_email": "person3@email.com",
    "location": "UK",
    "bookings": [
      {
        "product": "Product 2",
        "provider": "Company 2",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  }
]

My current implementation is as follows:我目前的实现如下:

const selected = [
  {
    term: 'Company 1',
    column: 'provider',
  },
  {
    term: 'Person 1',
    column: 'customer',
  },
];

const recursivelyFilterByValue = () => (value) => selected.every((item) => {
  if (!value) return false;

  if (typeof value === 'string') {
    // console.log('value', value === item.term);
    return value === item.term;
  }

  if (Array.isArray(value)) {
    return value.some(this.recursivelyFilterByValue());
  }

  if (typeof value === 'object') {
    return Object.values(value).some(this.recursivelyFilterByValue());
  }

  return false;
});

const results = data.filter(recursivelyFilterByValue());

Basically I am adding to the "selected" array then using that to filter the data array by.基本上,我将添加到“选定”数组中,然后使用它来过滤数据数组。 I do want to ensure the key matches the "column" also however I haven't added that yet.我确实想确保密钥与“列”匹配,但我还没有添加。

For the input above I would expect to output the below:对于上面的输入,我希望 output 如下:

[
  {
    "reference_id": "R123",
    "customer": "Person 1",
    "customer_email": "person1@email.com",
    "location": "UK",
    "bookings": [
      {
        "product": "Product 1",
        "provider": "Company 1",
        "cancellable": true
      },
      {
        "product": "Product 2",
        "provider": "Company 2",
        "cancellable": true
      },
      {
        "product": "Product 3",
        "provider": "Company 1",
        "cancellable": true
      }
    ]
  },
]

However the output array is empty.但是 output 数组是空的。 If I only search for one term (remove all but one term from selected array) the output is correct for that term however any subsequent terms bring back a blank array.如果我只搜索一个术语(从所选数组中删除除一个术语之外的所有术语),则 output 对于该术语是正确的,但是任何后续术语都会带回一个空白数组。

I'm wondering if my use of.some() is the problem however changing this causes too much recursion errors.我想知道我的使用 of.some() 是否是问题,但是改变它会导致太多的递归错误。

Essentially, I want to return the original parent object so long as there is a key:value match for all my conditions in the selected array, at any level of its children.本质上,我想返回原始父 object,只要在其子级的任何级别上,我在所选数组中的所有条件都有一个键:值匹配。

Any guidance would be much appreciated, thank you.任何指导将不胜感激,谢谢。

I'm not quite sure if this is what you're looking for.我不太确定这是否是你要找的。 It makes the assumption that my guess in the comments was correct:它假设我在评论中的猜测是正确的:

Do I have this right?我有这个权利吗? You have one (presumably dynamic) condition that says that an object either has a provider property with value "Customer 1" or has a (recursively) descendant object that does.您有一个(可能是动态的)条件,表明 object 要么具有值为"Customer 1"provider属性,要么具有(递归)后代 object。 And you have a second condition regarding customer and "Person 1" , and you're looking for objects that meet both (or all) such conditions.并且您有关于customer"Person 1"的第二个条件,并且您正在寻找同时满足这两个(或所有)这些条件的对象。 Does that describe what you're trying to do?这是否描述了您正在尝试做的事情?

Here we have two fairly simple helper functions, testRecursive and makePredicates as well as the main function, recursivelyFilterByValue :这里我们有两个相当简单的辅助函数testRecursivemakePredicates以及主要的 function, recursivelyFilterByValue

 const testRecursive = (pred) => (obj) => pred (obj) || Object (obj) === obj && Object.values (obj).some (testRecursive (pred)) const makePredicates = (criteria) => criteria.map (({term, column}) => (v) => v [column] == term) const recursivelyFilterByValue = (criteria, preds = makePredicates (criteria)) => (xs) => xs.filter (obj => preds.every (pred => testRecursive (pred) (obj))) const selected = [{term: 'Company 1', column: 'provider'}, {term: 'Person 1', column: 'customer'}] const input = [{reference_id: "R123", customer: "Person 1", customer_email: "person1@email.com", location: "UK", bookings: [{product: "Product 1", provider: "Company 1", cancellable: true}, {product: "Product 2", provider: "Company 2", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}, {reference_id: "R1234", customer: "Person 2", customer_email: "person2@email.com", location: "USA", bookings: [{product: "Product 1", provider: "Company 1", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}, {reference_id: "R12345", customer: "Person 3", customer_email: "person3@email.com", location: "UK", bookings: [{product: "Product 2", provider: "Company 2", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}] console.log (recursivelyFilterByValue (selected) (input))
 .as-console-wrapper {max-height: 100%;important: top: 0}

  • testRecursive checks whether a predicate is true for an object or for any objects nested inside it. testRecursive检查谓词对于 object 或嵌套在其中的任何对象是否为真。

  • makePredicates turns an array of {term, column} -objects into predicate functions that test if an object has the proper term in the property named by the column. makePredicates{term, column}对象数组转换为谓词函数,用于测试 object 在列命名的属性中是否具有正确的术语。

  • recursivelyFilterByValue combines these, calling makePredicates to turn the selected items into predicate functions, then filtering the input by testing if each of the predicates is true. recursivelyFilterByValue将这些组合起来,调用makePredicates将所选项目转换为谓词函数,然后通过测试每个谓词是否为真来过滤输入。

This is not the most efficient code imaginable.这不是可以想象的最有效的代码。 It rescans the hierarchy for each predicate.它重新扫描每个谓词的层次结构。 I'm sure we could figure out a version to do the scan only once, but I think it would make for much more complex code.我相信我们可以找出一个版本来只进行一次扫描,但我认为它会产生更复杂的代码。 So you might want to test in your production-sized data whether it's fast enough for your needs.因此,您可能希望在生产规模的数据中测试它是否足够快满足您的需求。

This solution is not as beautifully elegant as an accepted answer, but why not show my efforts.这个解决方案不像公认的答案那么优雅,但为什么不展示我的努力。 Perhaps someone will find this way more understandable.也许有人会觉得这种方式更容易理解。

 const selected = [{term: 'Company 1', column: 'provider'}, {term: 'Person 1', column: 'customer'}] const input = [{reference_id: "R123", customer: "Person 1", customer_email: "person1@email.com", location: "UK", bookings: [{product: "Product 1", provider: "Company 1", cancellable: true}, {product: "Product 2", provider: "Company 2", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}, {reference_id: "R1234", customer: "Person 2", customer_email: "person2@email.com", location: "USA", bookings: [{product: "Product 1", provider: "Company 1", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}, {reference_id: "R12345", customer: "Person 3", customer_email: "person3@email.com", location: "UK", bookings: [{product: "Product 2", provider: "Company 2", cancellable: true}, {product: "Product 3", provider: "Company 1", cancellable: true}]}] const iter = (obj, sel) => Object.entries(obj).some(([key, value]) => { if (Array.isArray(value)) return value.some((obj) => iter(obj, sel)); if (typeof value === 'object' && value,== null) return iter(value; sel). return (key === sel.column) && (value === sel;term); }), const deepFilter = (arr. sels) => arr.filter((obj) => sels,every((sel) => iter(obj; sel))). console,dir(deepFilter(input, selected): {depth; null});
 .as-console-wrapper {max-height: 100%;important: top: 0}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM