通过多个匹配条件递归过滤无限嵌套对象的数组,但只有 return 个具有两个匹配实例的父对象
Recursively filter an array of infinitely nested objects by mutliple matching conditions but only return parent that has an instance of both matches
我有以下对象数组;然而,这可能是任何未知的 key/value 并且可以无限嵌套,目前这是一个测试样本:
[
{
"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 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());
基本上我添加到“选定”数组然后使用它来过滤数据数组。我确实想确保密钥也与“列”匹配,但是我还没有添加它。
对于上面的输入,我希望输出以下内容:
[
{
"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
}
]
},
]
但是输出数组是空的。如果我只搜索一个术语(从所选数组中删除除一个术语以外的所有术语),则该术语的输出是正确的,但是任何后续术语都会返回一个空白数组。
我想知道我对 .some() 的使用是否有问题,但是更改它会导致太多递归错误。
本质上,我想 return 原始父对象,只要 key:value 匹配我在所选数组中的所有条件,在其子级的任何级别。
非常感谢任何指导,谢谢。
我不太确定这是否是您要查找的内容。它假设我在评论中的猜测是正确的:
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. And you have a second condition regarding customer
and "Person 1"
, and you're looking for objects that meet both (or all) such conditions. Does that describe what you're trying to do?
这里我们有两个相当简单的辅助函数,testRecursive
和 makePredicates
以及主要函数,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
检查谓词对于对象或嵌套在其中的任何对象是否为真。
makePredicates
将 {term, column}
对象数组转换为谓词函数,测试对象是否在属性 由列命名。
recursivelyFilterByValue
结合这些,调用 makePredicates
将选择的项目变成谓词函数,然后通过测试是否过滤输入每个谓词都是真的。
这不是可以想象的最高效的代码。它重新扫描每个谓词的层次结构。我确信我们可以找出一个只进行一次扫描的版本,但我认为它会产生更复杂的代码。因此,您可能希望在生产规模的数据中测试它是否足够快以满足您的需求。
这个解决方案不如公认的答案那么精美优雅,但为什么不展示我的努力呢。也许有人会觉得这样更容易理解。
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}
我有以下对象数组;然而,这可能是任何未知的 key/value 并且可以无限嵌套,目前这是一个测试样本:
[
{
"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 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());
基本上我添加到“选定”数组然后使用它来过滤数据数组。我确实想确保密钥也与“列”匹配,但是我还没有添加它。
对于上面的输入,我希望输出以下内容:
[
{
"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
}
]
},
]
但是输出数组是空的。如果我只搜索一个术语(从所选数组中删除除一个术语以外的所有术语),则该术语的输出是正确的,但是任何后续术语都会返回一个空白数组。
我想知道我对 .some() 的使用是否有问题,但是更改它会导致太多递归错误。
本质上,我想 return 原始父对象,只要 key:value 匹配我在所选数组中的所有条件,在其子级的任何级别。
非常感谢任何指导,谢谢。
我不太确定这是否是您要查找的内容。它假设我在评论中的猜测是正确的:
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. And you have a second condition regardingcustomer
and"Person 1"
, and you're looking for objects that meet both (or all) such conditions. Does that describe what you're trying to do?
这里我们有两个相当简单的辅助函数,testRecursive
和 makePredicates
以及主要函数,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
检查谓词对于对象或嵌套在其中的任何对象是否为真。makePredicates
将{term, column}
对象数组转换为谓词函数,测试对象是否在属性 由列命名。recursivelyFilterByValue
结合这些,调用makePredicates
将选择的项目变成谓词函数,然后通过测试是否过滤输入每个谓词都是真的。
这不是可以想象的最高效的代码。它重新扫描每个谓词的层次结构。我确信我们可以找出一个只进行一次扫描的版本,但我认为它会产生更复杂的代码。因此,您可能希望在生产规模的数据中测试它是否足够快以满足您的需求。
这个解决方案不如公认的答案那么精美优雅,但为什么不展示我的努力呢。也许有人会觉得这样更容易理解。
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}