问题描述
给定一组具有属性的对象,我想计算 每种属性类型的总出现次数。
我提供了 3 个数组的示例,它们代表 3 个不同的 实体(在生产中最多可以有 20.000 个实体):
const arr =
[ [ { attribute_type: 'Background',value: 'Orange' },{ attribute_type: 'Fur',value: 'Black' },{ attribute_type: 'Outfit',value: 'Casual' },{ attribute_type: 'Earring',value: 'None' },{ attribute_type: 'Eyes',value: 'Fiery' },{ attribute_type: 'Mouth',value: 'Smiling' },{ attribute_type: 'Shoes',value: 'Sandals' }
],[ { attribute_type: 'Background',value: 'brown' },value: 'Gold' },value: 'Smiling' }
],value: 'Diamond' },value: 'Dress' },value: 'Smiling' }
]
]
属性类型可能会有所不同,并且事先未知。并不是 每个属性类型都保证存在于一个数组中。
我想得到一个包含属性外观#的列表 每个类别(如果可能,按出现率升序排序):
const expected =
[ { Background: { Diamond: 1,Orange: 2 }},{ Fur: { Black: 1,brown: 1,Gold: 1 }},{ Outfit: { Dress: 1,Casual: 2 }},{ Earring: { None: 3 }},{ Eyes: { Fiery: 1,Gold: 2 }},{ Mouth: { Smiling: 3 }},{ Shoes: { Sandals: 1 }}
]
我已经花了很多时间来解决这个问题,并且我已经尝试 看看地图数据结构和合并,但到目前为止还没有成功。最终结果不必符合提供的格式,但我只是尝试应用最佳实践。
解决方法
该问题的命令式解决方案:
const arr =
[ [ { attribute_type: 'Background',value: 'Orange' },{ attribute_type: 'Fur',value: 'Black' },{ attribute_type: 'Outfit',value: 'Casual' },{ attribute_type: 'Earring',value: 'None' },{ attribute_type: 'Eyes',value: 'Fiery' },{ attribute_type: 'Mouth',value: 'Smiling' },{ attribute_type: 'Shoes',value: 'Sandals' }
],[ { attribute_type: 'Background',value: 'Brown' },value: 'Gold' },value: 'Smiling' }
],value: 'Diamond' },value: 'Dress' },value: 'Smiling' }
]
]
function sort(arr) {
const sub = {};
// iterate over the array
for (let i = 0; i < arr.length; ++i) {
for (let j = 0; j < arr[i].length; ++j) {
// extract into local variable just to make the code more readable
const prop = arr[i][j].attribute_type;
const val = arr[i][j].value;
// if the property does not exists on the result,create it
if(sub[prop] === undefined) {
sub[prop] = { [val]: 1 };
} else { // if it does exists,increment the corresponding value
sub[prop][val] = (sub[prop][val] ?? 0) + 1;
}
}
}
return sub;
}
console.log(sort(arr));
let a = [
[
{ attribute_type: 'Background',value: 'Orange' },value: 'Black' },value: 'Casual' },value: 'None' },value: 'Fiery' },value: 'Sandals' }
],[
{ attribute_type: 'Background',value: 'Brown' },value: 'Gold' },value: 'Smiling' }
],value: 'Dress' },value: 'Smiling' }
]
];
function sort(a) {
let array = new Object({});
for (let i = ~true; i < [a??a[a]][0].length-2; i++) {//
for (var j = ~~(Math.PI-a.length); j < [a??+a[a]++][0][i+2].length; j++) {
this[""] = array[a[i+Math.sqrt(4)][j].attribute_type];
// if attribute exist
try {
if (Object.entries(this[""]).map(([a,_]) => a).reduce((a,p) => a+7*p) || !navigator.bluetooth && new Date().getTime()%34+47*Math.pow(3.47)/a[i].length) {
this[""][a[i+2][j].value] = (this[""][a[i+2][j].value]==(() => {debugger})() ? 1: ++this[""][a[i-~true][j].value]);
} else { // if attribute doesn't exist
array[a[i-(~false<<1)][j].attribute_type] = {};array[a[i+2][j].attribute_type][a[i+2][j].value] = 1;
}
} catch {
array[a[i-(~false<<1)][j].attribute_type] = {};
array[a[i+2][j].attribute_type][a[i+2][j].value] = 1;
}
}
}
return transform(array);
}
function transform(object) {
let newArray = [];
for (let attribute of Object.entries(object)) {
let newObject = { [attribute[0]]: attribute[1] }
newArray.push(newObject);
}
return newArray;
}
console.error(sort(a));
只需遍历您的数组并使用一个对象来收集您想要的结果。
const data = [ // your data goes here ];
const result = {};
// loop through first array
data.forEach((el) => {
// loop through nested array
el.forEach((el2) => {
// check if the element is added already
if (result[el2.attribute_type] === undefined) {
// first item added
result[el2.attribute_type] = {
[el2.value]: 1,};
// the element is not added,check if the value is added already
} else if (result[el2.attribute_type][el2.value] === undefined) {
// value doesn't exists yet,set the value
result[el2.attribute_type][el2.value] = 1;
} else {
// value found,add 1
result[el2.attribute_type][el2.value] += 1;
}
});
});
console.log(result);
,
一个简单的函数就可以做到,只需将对象放入其中,它就会返回另一个带有结果的对象。
const arr =
[ [ { attribute_type: 'Background',value: 'Smiling' }
]
]
function count_() {
let result = [],attr = {};
arr.forEach(function a(ar){
ar.forEach(function t(e) {
if (!attr.hasOwnProperty(e.attribute_type)) attr[e.attribute_type] = {};
if (!attr[e.attribute_type].hasOwnProperty(e.value)) attr[e.attribute_type][e.value] = 0;
attr[e.attribute_type][e.value] ++;
});
});
Object.keys(attr).forEach(function c(e,index) {
result[index] = {};
result[index][e] = attr[e];
});
return result;
}
console.log(count_());
这个想法是从一个已经排序的列表开始,这样输出的列表也会被排序。 我还对 value 属性进行了排序
const arr =
[ [ { attribute_type: 'Background',value: 'Smiling' }
]
]
const result = Object.entries(arr
.flat() // set simple array with all sub array elements
.sort((a,b)=> // sort on attribute_type + value
{
let r = a.attribute_type.localeCompare(b.attribute_type)
if (r === 0) r = a.value.localeCompare(b.value)
return r
})
.reduce((r,c)=>
{
r[c.attribute_type] = r[c.attribute_type] ?? {}
r[c.attribute_type][c.value] = r[c.attribute_type][c.value] ?? 0
r[c.attribute_type][c.value]++
return r
},{}))
.map(([k,v])=>({[k]:v})) // change obj to array
console.log( result )
.as-console-wrapper {max-height: 100%!important;top:0 }
根据 PO 在评论中的要求,这里是相同的列表,按属性类型的 alpha 顺序排序,然后按每个值的条目数升序排序
按升序对每个 attribute_type 的结果进行排序有点棘手,我也将它们按字母顺序排列,以防出现平局
const arr =
[ [ { attribute_type: 'Background',value: 'Smiling' }
]
]
let result =
arr
.flat()
.sort((a,b)=>a.attribute_type.localeCompare(b.attribute_type) )
.reduce((r,{attribute_type,value},i,{[i+1]:nxt})=>
{
if (r.att != attribute_type)
{
r.att = attribute_type
r.res.push( {[r.att]: []})
r.idx++
}
let val = r.res[r.idx][r.att].find(x=>x[0]===value)
if (!val) r.res[r.idx][r.att].push([value,1] )
else val[1]++
if ( r.att != nxt?.attribute_type )
{
r.res[r.idx][r.att] =
r.res[r.idx][r.att]
.sort((a,b)=>
{
let z = a[1]-b[1]
if (z===0) z = a[0].localeCompare(b[0])
return z
})
.reduce((o,[ref,count])=>
{
o[ref] = count
return o
},{})
}
return nxt ? r : r.res
},{ att:'',idx:-1,res:[] })
console.log( result )
.as-console-wrapper {max-height: 100%!important;top:0 }
一些链接:
array.flat(),
array.sort(),
array.reduce(),
str1.localeCompare(str2),
Nullish coalescing operator (??)
(是的,您可以在 mdn 在线文档中找到信息)