如何根据项的 属性 键和值过滤数组项,过滤条件必须从另一个不同的数据池中获取?
How to filter array items, based on an item's property key and value, with filter criteria that has to be obtained from yet another varying data pool?
我面临着一个相当具有挑战性的问题,我只是想看看是否有更好的解决方案。
我有一个带有嵌套数组 data
的对象 lets call it mainObject
,如下所示:
{
"arrayInObject": [
{
"date": "2021-08-27T09:44:31.059Z",
"data": [
{
"ticketId": "abc",
"type": "Food",
"dateCreated": "2021-08-27T09:44:31.059Z"
},
{
"ticketId": "efg",
"type": "Drinks",
"dateCreated": "2021-08-27T09:44:31.059Z"
}
]
}
]
}
并有一个看起来像
的数组
const arrayOne = [
{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
},
{
id: "2",
label: "Food",
type: 1,
value: "2",
},
{
name: "1",
label: "Drinks",
type: 1,
value: "1",
},
];
我正在推动从不同函数中使用 0 for cinema name
、1 for Snackbar categories
类型从 arrayOne 中删除对象,因此 arrayOne 可以包含一个或多个类型 0 或类型 1 的对象
我然后像这样过滤所有类型为 1 的对象的数组...
const arrayOne = [
{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
},
{
id: "2",
label: "Food",
type: 1,
value: "2",
},
{
name: "1",
label: "Desserts",
type: 1,
value: "1",
},
];
const type = [1];
const filteredArrayForKey = arrayOne.filter(item =>
(type.indexOf(item.type) > -1)
);
const mapKeyToArray = filteredArrayForKey.map(item =>
item.label);
其中returns一个字符串数组包含:
mapKeyToArray=["Food", "Desserts"]
我感到困惑的地方是:
我想过滤包含嵌套数组的第三个对象,并将这些值推送到 arrayOne
以用作 type: 2
来过滤 mainObject 中的数组。
const ticketIds = {
results: 2,
ticketIds: [
{
ticketId: 'abc',
},
{
ticketId: 'xyz',
},
],
};
const mapKeyToArray = ["abc", "xyz"]
所以预期的结果是 mainObject 中的数组只返回包含 type: 1 或 type: 2 在 mapKeyToArray
中找到的对象
const mapKeyToArray = ["Food", "Desserts", "abc", "xyz"]
{
"arrayInObject": [
{
"date": "2021-08-27T09:44:31.059Z",
"data": [
{
"ticketId": "tyu",
"type": "Food",
"dateCreated": "2021-08-27T09:44:31.059Z"
},
{
"ticketId": "abc",
"type": "Drinks",
"dateCreated": "2021-08-27T09:44:31.059Z"
}
]
}
]
}
非常简单,如果主对象中的初始数组不包含 mapKeyToArray const 中的任何字符串值,则重建数组中不会返回它们。示例:
const mapKeyToArray = ["abc", "Desserts"]
{
"arrayInObject": [
{
"date": "2021-08-27T09:44:31.059Z",
"data": [
{
"ticketId": "abc",
"type": "Drinks",
"dateCreated": "2021-08-27T09:44:31.059Z"
}
]
}
]
}
OP: "Let me sum it up. I am trying to only return the objects within the array in mainObject by either the label of any object in arrayOne containing the key type: 1 OR if the object in the array in mainObject contains a ticketId from the arrary in the ticketId object."
这 wish/requirement 直接导致基于规则的布尔 filter/validation 方法,该方法应尽可能通用于操作的数据结构。在这种情况下,OP 希望实现完全基于 OR
filter
ing, which means some
values
每个 out of some
具体属性-key
s.
对于示例数据项,例如...
{
ticketId: "abc",
type: "Food",
dateCreated: "2021-08-27T09:44:31.059Z"
}
... 和 list/array 基本规则集...
[{
key: 'type',
values: [
'Desserts',
]
}, {
key: 'ticketId',
values: [
'abc',
]
}]
...已经可以编写一种通用方法,该方法针对任何提供的数据项测试规则集中的任何规则...
const matchingRules = [{
key: 'type',
values: [
'Desserts',
]
}, {
key: 'ticketId',
values: [
'abc',
]
}];
let dataItem = {
ticketId: "abc",
type: "Food",
dateCreated: "2021-08-27T09:44:31.059Z"
};
console.log(
'does', dataItem, 'match some of', matchingRules, '?',
matchingRules.some(rule =>
rule.values.some(value =>
dataItem[rule.key] === value
)
)
);
dataItem = {
ticketId: "efg",
type: "Drinks",
dateCreated: "2021-08-27T09:44:31.059Z",
};
console.log(
'does', dataItem, 'match some of', matchingRules, '?',
matchingRules.some(rule =>
rule.values.some(value =>
dataItem[rule.key] === value
)
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
然后只剩下编写两个函数,每个函数通过 reduce
.
根据 OP 提供的数据创建特定的匹配或过滤规则
并且为了不总是改变要对其进行操作的原始数据的子数组,另外还需要实现一个提供 deep/save enough copy of the original data 结构的函数。
function createTypeFilterRuleFromCategoryItems(list) {
return list
// create `type` based filter rule
// upon a filter condition which
// targets a category item's `type`.
.reduce((rule, item) => {
if (item.type === 1) {
rule.values.push(item.label)
}
return rule;
}, { key: 'type', values: [] });
}
const categoryList = [{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
}, {
id: "2",
label: "Food",
type: 1,
value: "2",
}, {
name: "1",
label: "Drinks",
type: 1,
value: "1",
}];
console.log(
'createTypeFilterRuleFromCategoryItems(categoryList) ...',
createTypeFilterRuleFromCategoryItems(categoryList)
);
function createFilterRuleFromRandomBlobByKeys(blob, listKey, itemKey/*, targetKey*/) {
return (blob?.[listKey] ?? [])
// create filter rule based on an
// additionally provided key set.
.reduce((rule, item) => {
if (item.hasOwnProperty(itemKey)) {
rule.values.push(item[itemKey])
}
return rule;
}, { key: itemKey/*targetKey*/, values: [] });
}
const randomBlob = {
results: 2,
ticketIds: [{
ticketId: 'abc',
}, {
ticketId: 'xyz',
}],
};
console.log(
"createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId') ...",
createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId')
);
function createDataCopy(blob) {
const copy = { arrayInObject: [ { ...blob.arrayInObject[0] } ] };
copy.arrayInObject[0].data =
copy.arrayInObject[0].data.map(item => ({ ...item }));
return copy;
}
const sampleData = {
arrayInObject: [{
date: "2021-08-27T09:44:31.059Z",
data: [{
ticketId: "abc",
type: "Food",
dateCreated: "2021-08-27T09:44:31.059Z",
}, {
ticketId: "efg",
type: "Drinks",
dateCreated: "2021-08-27T09:44:31.059Z",
}],
}],
};
const dataCopy = createDataCopy(sampleData);
// console.log({ dataCopy });
function matchItemByBoundFilterRules(item) {
const ruleList = this;
return ruleList.some(rule =>
rule.values.some(value =>
item[rule.key] === value
)
);
}
dataCopy.arrayInObject[0].data =
dataCopy.arrayInObject[0].data.filter(matchItemByBoundFilterRules, [
createTypeFilterRuleFromCategoryItems(categoryList),
createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId'),
]);
console.log('... after first filtering ...', { dataCopy });
dataCopy.arrayInObject[0].data =
dataCopy.arrayInObject[0].data.filter(matchItemByBoundFilterRules, [{
key: 'type',
values: [
'Desserts',
]
}, {
key: 'ticketId',
values: [
'abc',
]
}]);
console.log('... after second filtering ...', { dataCopy });
console.log('... unmutaded original data ...', { sampleData });
.as-console-wrapper { min-height: 100%!important; top: 0; }
编辑
OP: "Nice approach. Yeah sure, please can you tell me the approaching you took to getting the matchingRules array from arrayOne and TicketIds?"
实际上这些方法已经 provided/shown 上面的示例代码。
然而,两者都将在此得到更详细的解释。
无论要处理什么样的源数据结构,单个规则的目标结构总是需要采用这种形式...
{
key: <propertyName:String>,
values: [
<arrayItem:any>[, <arrayItem:any>[, ...] ]
]
}
...这意味着创建此类规则的函数的实现始终由必须从中派生此类规则的数据结构决定。
对于第一个数据结构的情况,OP 指的是 arrayOne
...
[{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
}, {
id: "2",
label: "Food",
type: 1,
value: "2",
}, {
name: "1",
label: "Drinks",
type: 1,
value: "1",
}];
... OP 1stly 想要过滤所有项目,其中每个项目的 type
等于数值 1
。然后,OP 在第二步中从每个过滤的项目中,希望将项目的 label
值作为 type
特定 match/filter 规则的 values
数组的一部分。因此规则可能看起来像这样...
{
"key": "type",
"values": [
"Food",
"Drinks"
]
}
... 并且规则创建函数很可能会利用 Array.prototype.reduce
并可能按如下方式实现 ...
function createTypeFilterRuleFromCategoryItems(list) {
return list
// create `type` based filter rule
// upon a filter condition which
// targets a category item's `type`.
.reduce((rule, item) => {
if (item.type === 1) {
rule.values.push(item.label)
}
return rule;
}, { key: 'type', values: [] });
}
const categoryList = [{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
}, {
id: "2",
label: "Food",
type: 1,
value: "2",
}, {
name: "1",
label: "Drinks",
type: 1,
value: "1",
}];
console.log(
'createTypeFilterRuleFromCategoryItems(categoryList) ...',
createTypeFilterRuleFromCategoryItems(categoryList)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
至于第二种情况,OP 将数据结构称为 TicketIds
但是,从问题的原始状态和编辑状态来看,似乎是以下结构的任何通用 blob ...
const randomBlob = {
results: 2,
ticketIds: [{
ticketId: 'abc',
}, {
ticketId: 'xyz',
}],
};
...OP 想要创建一个基于 ticketId
的 match/filter 规则,如下所示...
{
"key": "ticketId",
"values": [
"abc",
"xyz"
]
}
基于这样的数据结构,可能只是 属性 名称不同,但类型基稳定,规则创建函数可以以更通用的方式实现,例如基于reduce
和Object.prototype.hasOwnProperty
,像下面这一个...
function createFilterRuleFromRandomBlobByKeys(blob, listKey, itemKey/*, targetKey*/) {
return (blob?.[listKey] ?? [])
// create filter rule based on an
// additionally provided key set.
.reduce((rule, item) => {
if (item.hasOwnProperty(itemKey)) {
rule.values.push(item[itemKey])
}
return rule;
}, { key: itemKey/*targetKey*/, values: [] });
}
const randomBlob = {
results: 2,
ticketIds: [{
ticketId: 'abc',
}, {
ticketId: 'xyz',
}],
};
console.log(
"createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId') ...",
createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
我面临着一个相当具有挑战性的问题,我只是想看看是否有更好的解决方案。
我有一个带有嵌套数组 data
的对象 lets call it mainObject
,如下所示:
{
"arrayInObject": [
{
"date": "2021-08-27T09:44:31.059Z",
"data": [
{
"ticketId": "abc",
"type": "Food",
"dateCreated": "2021-08-27T09:44:31.059Z"
},
{
"ticketId": "efg",
"type": "Drinks",
"dateCreated": "2021-08-27T09:44:31.059Z"
}
]
}
]
}
并有一个看起来像
的数组const arrayOne = [
{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
},
{
id: "2",
label: "Food",
type: 1,
value: "2",
},
{
name: "1",
label: "Drinks",
type: 1,
value: "1",
},
];
我正在推动从不同函数中使用 0 for cinema name
、1 for Snackbar categories
类型从 arrayOne 中删除对象,因此 arrayOne 可以包含一个或多个类型 0 或类型 1 的对象
我然后像这样过滤所有类型为 1 的对象的数组...
const arrayOne = [
{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
},
{
id: "2",
label: "Food",
type: 1,
value: "2",
},
{
name: "1",
label: "Desserts",
type: 1,
value: "1",
},
];
const type = [1];
const filteredArrayForKey = arrayOne.filter(item =>
(type.indexOf(item.type) > -1)
);
const mapKeyToArray = filteredArrayForKey.map(item =>
item.label);
其中returns一个字符串数组包含:
mapKeyToArray=["Food", "Desserts"]
我感到困惑的地方是:
我想过滤包含嵌套数组的第三个对象,并将这些值推送到 arrayOne
以用作 type: 2
来过滤 mainObject 中的数组。
const ticketIds = {
results: 2,
ticketIds: [
{
ticketId: 'abc',
},
{
ticketId: 'xyz',
},
],
};
const mapKeyToArray = ["abc", "xyz"]
所以预期的结果是 mainObject 中的数组只返回包含 type: 1 或 type: 2 在 mapKeyToArray
中找到的对象const mapKeyToArray = ["Food", "Desserts", "abc", "xyz"]
{
"arrayInObject": [
{
"date": "2021-08-27T09:44:31.059Z",
"data": [
{
"ticketId": "tyu",
"type": "Food",
"dateCreated": "2021-08-27T09:44:31.059Z"
},
{
"ticketId": "abc",
"type": "Drinks",
"dateCreated": "2021-08-27T09:44:31.059Z"
}
]
}
]
}
非常简单,如果主对象中的初始数组不包含 mapKeyToArray const 中的任何字符串值,则重建数组中不会返回它们。示例:
const mapKeyToArray = ["abc", "Desserts"]
{
"arrayInObject": [
{
"date": "2021-08-27T09:44:31.059Z",
"data": [
{
"ticketId": "abc",
"type": "Drinks",
"dateCreated": "2021-08-27T09:44:31.059Z"
}
]
}
]
}
OP: "Let me sum it up. I am trying to only return the objects within the array in mainObject by either the label of any object in arrayOne containing the key type: 1 OR if the object in the array in mainObject contains a ticketId from the arrary in the ticketId object."
这 wish/requirement 直接导致基于规则的布尔 filter/validation 方法,该方法应尽可能通用于操作的数据结构。在这种情况下,OP 希望实现完全基于 OR
filter
ing, which means some
values
每个 out of some
具体属性-key
s.
对于示例数据项,例如...
{
ticketId: "abc",
type: "Food",
dateCreated: "2021-08-27T09:44:31.059Z"
}
... 和 list/array 基本规则集...
[{
key: 'type',
values: [
'Desserts',
]
}, {
key: 'ticketId',
values: [
'abc',
]
}]
...已经可以编写一种通用方法,该方法针对任何提供的数据项测试规则集中的任何规则...
const matchingRules = [{
key: 'type',
values: [
'Desserts',
]
}, {
key: 'ticketId',
values: [
'abc',
]
}];
let dataItem = {
ticketId: "abc",
type: "Food",
dateCreated: "2021-08-27T09:44:31.059Z"
};
console.log(
'does', dataItem, 'match some of', matchingRules, '?',
matchingRules.some(rule =>
rule.values.some(value =>
dataItem[rule.key] === value
)
)
);
dataItem = {
ticketId: "efg",
type: "Drinks",
dateCreated: "2021-08-27T09:44:31.059Z",
};
console.log(
'does', dataItem, 'match some of', matchingRules, '?',
matchingRules.some(rule =>
rule.values.some(value =>
dataItem[rule.key] === value
)
)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
然后只剩下编写两个函数,每个函数通过 reduce
.
并且为了不总是改变要对其进行操作的原始数据的子数组,另外还需要实现一个提供 deep/save enough copy of the original data 结构的函数。
function createTypeFilterRuleFromCategoryItems(list) {
return list
// create `type` based filter rule
// upon a filter condition which
// targets a category item's `type`.
.reduce((rule, item) => {
if (item.type === 1) {
rule.values.push(item.label)
}
return rule;
}, { key: 'type', values: [] });
}
const categoryList = [{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
}, {
id: "2",
label: "Food",
type: 1,
value: "2",
}, {
name: "1",
label: "Drinks",
type: 1,
value: "1",
}];
console.log(
'createTypeFilterRuleFromCategoryItems(categoryList) ...',
createTypeFilterRuleFromCategoryItems(categoryList)
);
function createFilterRuleFromRandomBlobByKeys(blob, listKey, itemKey/*, targetKey*/) {
return (blob?.[listKey] ?? [])
// create filter rule based on an
// additionally provided key set.
.reduce((rule, item) => {
if (item.hasOwnProperty(itemKey)) {
rule.values.push(item[itemKey])
}
return rule;
}, { key: itemKey/*targetKey*/, values: [] });
}
const randomBlob = {
results: 2,
ticketIds: [{
ticketId: 'abc',
}, {
ticketId: 'xyz',
}],
};
console.log(
"createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId') ...",
createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId')
);
function createDataCopy(blob) {
const copy = { arrayInObject: [ { ...blob.arrayInObject[0] } ] };
copy.arrayInObject[0].data =
copy.arrayInObject[0].data.map(item => ({ ...item }));
return copy;
}
const sampleData = {
arrayInObject: [{
date: "2021-08-27T09:44:31.059Z",
data: [{
ticketId: "abc",
type: "Food",
dateCreated: "2021-08-27T09:44:31.059Z",
}, {
ticketId: "efg",
type: "Drinks",
dateCreated: "2021-08-27T09:44:31.059Z",
}],
}],
};
const dataCopy = createDataCopy(sampleData);
// console.log({ dataCopy });
function matchItemByBoundFilterRules(item) {
const ruleList = this;
return ruleList.some(rule =>
rule.values.some(value =>
item[rule.key] === value
)
);
}
dataCopy.arrayInObject[0].data =
dataCopy.arrayInObject[0].data.filter(matchItemByBoundFilterRules, [
createTypeFilterRuleFromCategoryItems(categoryList),
createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId'),
]);
console.log('... after first filtering ...', { dataCopy });
dataCopy.arrayInObject[0].data =
dataCopy.arrayInObject[0].data.filter(matchItemByBoundFilterRules, [{
key: 'type',
values: [
'Desserts',
]
}, {
key: 'ticketId',
values: [
'abc',
]
}]);
console.log('... after second filtering ...', { dataCopy });
console.log('... unmutaded original data ...', { sampleData });
.as-console-wrapper { min-height: 100%!important; top: 0; }
编辑
OP: "Nice approach. Yeah sure, please can you tell me the approaching you took to getting the matchingRules array from arrayOne and TicketIds?"
实际上这些方法已经 provided/shown 上面的示例代码。
然而,两者都将在此得到更详细的解释。
无论要处理什么样的源数据结构,单个规则的目标结构总是需要采用这种形式...
{
key: <propertyName:String>,
values: [
<arrayItem:any>[, <arrayItem:any>[, ...] ]
]
}
...这意味着创建此类规则的函数的实现始终由必须从中派生此类规则的数据结构决定。
对于第一个数据结构的情况,OP 指的是 arrayOne
...
[{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
}, {
id: "2",
label: "Food",
type: 1,
value: "2",
}, {
name: "1",
label: "Drinks",
type: 1,
value: "1",
}];
... OP 1stly 想要过滤所有项目,其中每个项目的 type
等于数值 1
。然后,OP 在第二步中从每个过滤的项目中,希望将项目的 label
值作为 type
特定 match/filter 规则的 values
数组的一部分。因此规则可能看起来像这样...
{
"key": "type",
"values": [
"Food",
"Drinks"
]
}
... 并且规则创建函数很可能会利用 Array.prototype.reduce
并可能按如下方式实现 ...
function createTypeFilterRuleFromCategoryItems(list) {
return list
// create `type` based filter rule
// upon a filter condition which
// targets a category item's `type`.
.reduce((rule, item) => {
if (item.type === 1) {
rule.values.push(item.label)
}
return rule;
}, { key: 'type', values: [] });
}
const categoryList = [{
label: "Cinema",
type: 0,
value: "aaaa-bbbb-cccc",
}, {
id: "2",
label: "Food",
type: 1,
value: "2",
}, {
name: "1",
label: "Drinks",
type: 1,
value: "1",
}];
console.log(
'createTypeFilterRuleFromCategoryItems(categoryList) ...',
createTypeFilterRuleFromCategoryItems(categoryList)
);
.as-console-wrapper { min-height: 100%!important; top: 0; }
至于第二种情况,OP 将数据结构称为 TicketIds
但是,从问题的原始状态和编辑状态来看,似乎是以下结构的任何通用 blob ...
const randomBlob = {
results: 2,
ticketIds: [{
ticketId: 'abc',
}, {
ticketId: 'xyz',
}],
};
...OP 想要创建一个基于 ticketId
的 match/filter 规则,如下所示...
{
"key": "ticketId",
"values": [
"abc",
"xyz"
]
}
基于这样的数据结构,可能只是 属性 名称不同,但类型基稳定,规则创建函数可以以更通用的方式实现,例如基于reduce
和Object.prototype.hasOwnProperty
,像下面这一个...
function createFilterRuleFromRandomBlobByKeys(blob, listKey, itemKey/*, targetKey*/) {
return (blob?.[listKey] ?? [])
// create filter rule based on an
// additionally provided key set.
.reduce((rule, item) => {
if (item.hasOwnProperty(itemKey)) {
rule.values.push(item[itemKey])
}
return rule;
}, { key: itemKey/*targetKey*/, values: [] });
}
const randomBlob = {
results: 2,
ticketIds: [{
ticketId: 'abc',
}, {
ticketId: 'xyz',
}],
};
console.log(
"createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId') ...",
createFilterRuleFromRandomBlobByKeys(randomBlob, 'ticketIds', 'ticketId')
);
.as-console-wrapper { min-height: 100%!important; top: 0; }