2023新版本:可以根据第三个参数来控制生成二维数组还是对象
/**
* 根据数组中相同的属性名进行合并生成二维数组或者对象
*
* @param {array} arrayList 要排序的数组
* @param {string} propertyName 根据此属性名相同的进行合并
* @param {string} outputType array|object array则生成二维数组,否则生成对象形式
*
*/
groupByProperty(arrayList, propertyName, outputType) {
if (outputType === "array") {
return Object.values(
arrayList.reduce(function(result, obj) {
let key = obj[propertyName];
if (!result[key]) {
result[key] = [];
}
result[key].push(obj);
return result;
}, {})
);
} else {
return arrayList.reduce(function(result, obj) {
let key = obj[propertyName];
if (!result[key]) {
result[key] = [];
}
result[key].push(obj);
return result;
}, {});
}
},
以下为原答案:
项目中遇到一个需求,后端返回一个对象数组,要根据数组中的某个属性,相同的放进同一个数组中,生成一个二维数组来循环展示出来。
得到的是这样
我们要将这个数组中对象的name相同的放同一个数组中,现在网上能百度到的方法大多都是下面这个,而且也没有注释,变量名起的也看不太懂,我这里逐行注释一下。
/**
* @param {array} arr 要排序的数组
* @param {string} str 根据此属性名相同的进行合并
*
*/
sortArr(arr, str) {
var _arr = [],//二维数组
_t = [],//str属性名相同的先放进这个临时数组
_tmp;//临时属性名
//先按照str排序,把相同str的放一起(关键步骤)
arr.sort(function (a, b) {
var s = a[str],
t = b[str];
return s > t ? -1 : 1;
});
if (arr.length) {
//先取出数组中第一个str属性名存进 _tmp
_tmp = arr[0][str];
}
for (var i in arr) {
//如果和第一个存的属性名相同就push进临时数组中
if (arr[i][str] === _tmp) {
_t.push(arr[i]);
} else {
// 如果不同,拿到第一个不同的属性名赋值给 _tmp 临时属性名
_tmp = arr[i][str];
//把之前相同的放入最终的_arr
_arr.push(_t);
//把临时数组赋值为第一个不同的对象,(关键)否则下一次循环 i+1 就丢失一个对象
_t = [arr[i]];
}
}
//把最后一个集合放到_arr
_arr.push(_t);
return _arr;
},
这个方法也可以完成上边的需求,但是会有一个bug, 就是在第一步用.sort(),进行排序时,这个方法会先将属性名按照字符编码的顺序进行排序,这样就会打乱数组的顺序。举个例子:
我希望的name顺序是张三,李四,王五,a赵六。但是用Array.sort() ,排序后的输出是:
可以看到a赵六这个名字跑到了最前边,就是因为 a 的字符编码比 张 的字符编码靠前。这就导致了经过Array.sort() ,排序后的数组会被不确定的打乱顺序。
所以这种方法并不符合我的需求,我们来看第二种方法:
/**
* @param {array} arr 要排序的数组
* @param {string} str 根据此属性名相同的进行合并
*
*/
sortArr(arr, str) {
var _arr = [] //最终的二维数组
//取出所有要合并的属性名
let strArr = arr.map(item=>{
return item[str]
})
//对属性名进行一次去重
strArr = Array.from(new Set(strArr))
//先遍历属性名数组
strArr.map(strItem =>{
let temporaryArr = []
//遍历所有的数组
arr.forEach((objectItem)=>{
//如果 strItem 和数组中的属性名相同就push进临时数组
if(strItem == objectItem[str]){
temporaryArr.push(objectItem)
}
})
//在数组循环结束后,将临时数组push进二维数组中
_arr.push(temporaryArr)
})
return _arr
},
这个的思路是
1.先拿到数组中所有的str,然后去重,得到一个属性名数组,
2.遍历arr根据相同的str,push进一个临时数组,
3.遍历开始时先将临时数组赋空,结束时将临时数组push进二维数组中。
这样就会根据属性名第一个出现的顺序来生成数组了,不会被打乱顺序。
第二种方法也可以在遍历所有数组的时候对数据进行一次简单的处理再push进数组中。