使用过滤和优先化以及最小循环将数组转换为对象
Convert Array into Object with filtering and prioritizatoin and minimum loops
给定一个数组
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]
我的最终目标是得到这个:
{
welcome: {
landscape: ['Welcome'],
portrait: ['Bonjour bonjour']
}
}
为此,我需要转换成 {typeName: {orientation: value[]}}
这样的对象,如下所示:
// This is NOT what I want, it's just an intermediate form -- keep reading
{
welcome: {
landscape: ['Welcome', 'Bonjour'],
portrait: ['Bonjour bonjour']
}
}
但 包括优先级:如果数组中存在 languageId=1,则忽略特定类型名称、方向的剩余值。在上面的示例中应该只有 ['Welcome'] 因为是 languageId=1,所以 'Bonjour' 可以忽略,但是如果缺少 languageId=1,那么任何值都可以被添加 (welcome.portrait).
通过交流我没有遇到任何问题..这样做是想 .reduce() 方法
array.reduce((prev, current) => ({
...prev,
[current.typeName]: {
...prev[current.typeName],
[current.orientation]: [
...(((prev[current.typeName] || {})[current.orientation]) || []),
current.value
]
}
}), {});
但是我只能通过也在其中循环的过滤来确定优先级。到目前为止没问题,但是如果数组非常大 - 性能会受到影响
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]
const result = array
.filter((item) => {
return item.languageId === 1 ||
!array.some((innerItem) => ( //Inner loop that I want to avoid
innerItem.typeName === item.typeName &&
innerItem.orientation === item.orientation &&
innerItem.languageId === 1
))
})
.reduce((prev, current) => ({
...prev,
[current.typeName]: {
...prev[current.typeName],
[current.orientation]: [
...(((prev[current.typeName] || {})[current.orientation]) || []),
current.value
]
}
}), {});
console.log(result)
所以问题是避免内循环的最佳方法是什么?
您可以对 prio 项使用 Set
并防止向数组中添加更多项。
const
array = [{ typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome' }, { typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour' }, { typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour' }],
hasPrio = new Set,
result = array.reduce((r, { typeName, orientation, languageId, value }) => {
var key = `${typeName}|${orientation}`;
if (!r[typeName]) r[typeName] = {};
if (languageId === 1) {
r[typeName][orientation] = [value];
hasPrio.add(key);
} else if (!hasPrio.has(key)) {
if (!r[typeName][orientation]) r[typeName][orientation] = [];
r[typeName][orientation].push(value);
}
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
您可以使用 Set
来跟踪您是否看到了特定 typeName
和 orientation
的 languageId === 1
。 Set
是 required to have sublinear performance:
Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection.
所以 Set
将优于内部循环。 (如果你的键是字符串,你也可以使用一个对象,它们在我下面的例子中。如果你这样做,用 Object.create(null)
创建它,这样它就不会继承自 Object.prototype
。)
然后只是一个简单的循环而不是 reduce
:
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, { typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Bon dia'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]
const sawPriority = new Set();
const result = {};
for (const {typeName, orientation, languageId, value} of array) {
const key = `${typeName}-${orientation}`;
const isPriority = languageId === 1;
let entry = result[typeName];
if (!entry) {
// No entry yet, easy peasy
entry = result[typeName] = {[orientation]: [value]};
if (isPriority) {
sawPriority.add(key);
}
} else {
const hasPriority = sawPriority.has(key);
if (hasPriority === isPriority) {
// Either the first for a new orientation, or a subsequent entry that
// matches the priority
const inner = entry[orientation];
if (inner) {
// Subsequent, add
inner.push(value);
} else {
// First for orientation, create
entry[orientation] = [value];
}
} else if (isPriority) {
// It's a new priority entry, overwrite
entry[orientation] = [value];
sawPriority.add(key);
}
}
}
console.log(result)
(在评论中,您提到如果有多个 languageId === 1
条目,它们应该构建在一个数组中,因此我在上面包含了第二个条目以表明其有效。)
在上面,我正在比较两个布尔值,如下所示:
if (hasPriority === isPriority) {
之所以可行,是因为我 知道 它们实际上都是布尔值,而不仅仅是真值或假值,因为 isPriority
是 [=24= 的结果] 比较(保证为布尔值)和 hasPriority
是在 Set
上调用 has
的结果(保证为布尔值)。
如果有任何关于其中一个可能只是真值还是假值而不是绝对 true
或 false
的问题,我会使用 !
以确保它们是布尔值:
if (!hasPriority === !isPriority) {
观看现场演示 here
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}];
let obj = array.reduce( (acc, {orientation, value})=>{
acc[orientation] = (acc[orientation] || []).concat(value);
return acc;
}, {});
output = {};
output[array[0].typeName] = obj;
console.log(output);
给定一个数组
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]
我的最终目标是得到这个:
{
welcome: {
landscape: ['Welcome'],
portrait: ['Bonjour bonjour']
}
}
为此,我需要转换成 {typeName: {orientation: value[]}}
这样的对象,如下所示:
// This is NOT what I want, it's just an intermediate form -- keep reading
{
welcome: {
landscape: ['Welcome', 'Bonjour'],
portrait: ['Bonjour bonjour']
}
}
但 包括优先级:如果数组中存在 languageId=1,则忽略特定类型名称、方向的剩余值。在上面的示例中应该只有 ['Welcome'] 因为是 languageId=1,所以 'Bonjour' 可以忽略,但是如果缺少 languageId=1,那么任何值都可以被添加 (welcome.portrait).
通过交流我没有遇到任何问题..这样做是想 .reduce() 方法
array.reduce((prev, current) => ({
...prev,
[current.typeName]: {
...prev[current.typeName],
[current.orientation]: [
...(((prev[current.typeName] || {})[current.orientation]) || []),
current.value
]
}
}), {});
但是我只能通过也在其中循环的过滤来确定优先级。到目前为止没问题,但是如果数组非常大 - 性能会受到影响
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]
const result = array
.filter((item) => {
return item.languageId === 1 ||
!array.some((innerItem) => ( //Inner loop that I want to avoid
innerItem.typeName === item.typeName &&
innerItem.orientation === item.orientation &&
innerItem.languageId === 1
))
})
.reduce((prev, current) => ({
...prev,
[current.typeName]: {
...prev[current.typeName],
[current.orientation]: [
...(((prev[current.typeName] || {})[current.orientation]) || []),
current.value
]
}
}), {});
console.log(result)
所以问题是避免内循环的最佳方法是什么?
您可以对 prio 项使用 Set
并防止向数组中添加更多项。
const
array = [{ typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome' }, { typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour' }, { typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour' }],
hasPrio = new Set,
result = array.reduce((r, { typeName, orientation, languageId, value }) => {
var key = `${typeName}|${orientation}`;
if (!r[typeName]) r[typeName] = {};
if (languageId === 1) {
r[typeName][orientation] = [value];
hasPrio.add(key);
} else if (!hasPrio.has(key)) {
if (!r[typeName][orientation]) r[typeName][orientation] = [];
r[typeName][orientation].push(value);
}
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
您可以使用 Set
来跟踪您是否看到了特定 typeName
和 orientation
的 languageId === 1
。 Set
是 required to have sublinear performance:
Set objects must be implemented using either hash tables or other mechanisms that, on average, provide access times that are sublinear on the number of elements in the collection.
所以 Set
将优于内部循环。 (如果你的键是字符串,你也可以使用一个对象,它们在我下面的例子中。如果你这样做,用 Object.create(null)
创建它,这样它就不会继承自 Object.prototype
。)
然后只是一个简单的循环而不是 reduce
:
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, { typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Bon dia'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}]
const sawPriority = new Set();
const result = {};
for (const {typeName, orientation, languageId, value} of array) {
const key = `${typeName}-${orientation}`;
const isPriority = languageId === 1;
let entry = result[typeName];
if (!entry) {
// No entry yet, easy peasy
entry = result[typeName] = {[orientation]: [value]};
if (isPriority) {
sawPriority.add(key);
}
} else {
const hasPriority = sawPriority.has(key);
if (hasPriority === isPriority) {
// Either the first for a new orientation, or a subsequent entry that
// matches the priority
const inner = entry[orientation];
if (inner) {
// Subsequent, add
inner.push(value);
} else {
// First for orientation, create
entry[orientation] = [value];
}
} else if (isPriority) {
// It's a new priority entry, overwrite
entry[orientation] = [value];
sawPriority.add(key);
}
}
}
console.log(result)
(在评论中,您提到如果有多个 languageId === 1
条目,它们应该构建在一个数组中,因此我在上面包含了第二个条目以表明其有效。)
在上面,我正在比较两个布尔值,如下所示:
if (hasPriority === isPriority) {
之所以可行,是因为我 知道 它们实际上都是布尔值,而不仅仅是真值或假值,因为 isPriority
是 [=24= 的结果] 比较(保证为布尔值)和 hasPriority
是在 Set
上调用 has
的结果(保证为布尔值)。
如果有任何关于其中一个可能只是真值还是假值而不是绝对 true
或 false
的问题,我会使用 !
以确保它们是布尔值:
if (!hasPriority === !isPriority) {
观看现场演示 here
const array = [{
typeName: 'welcome', orientation: 'landscape', languageId: 1, value: 'Welcome'
}, {
typeName: 'welcome', orientation: 'landscape', languageId: 2, value: 'Bonjour'
}, {
typeName: 'welcome', orientation: 'portrait', languageId: 2, value: 'Bonjour bonjour'
}];
let obj = array.reduce( (acc, {orientation, value})=>{
acc[orientation] = (acc[orientation] || []).concat(value);
return acc;
}, {});
output = {};
output[array[0].typeName] = obj;
console.log(output);