Javascript 按两个属性对 JSON 对象进行分组并计数
Javascript group a JSON object by two properties and count
我有一个这样的 JSON 对象:
long_array =
[
{"location":"Kirrawee","identity_long":"student"},
{"location":"Kirrawee","identity_long":"visitor"},
{"location":"Kirrawee","identity_long":"visitor"},
{"location":"Kirrawee","identity_long":"worker"},
{"location":"Sutherland","identity_long":"student"},
{"location":"Sutherland","identity_long":"resident"},
{"location":"Sutherland","identity_long":"worker"},
{"location":"Sutherland","identity_long":"resident"},
{"location":"Miranda","identity_long":"resident"},
{"location":"Miranda","identity_long":"worker"},
{"location":"Miranda","identity_long":"student"},
{"location":"Miranda","identity_long":""},
{"location":"Miranda","identity_long":"worker"},
{"location":"Miranda","identity_long":"resident"}
];
我想要实现的是一个像下面这样的对象:
grouped_and_counted_location_and_identity =
[
{"location":"Kirrawee","identity":"student","count":1},
{"location":"Kirrawee","identity":"visitor","count":2},
{"location":"Kirrawee","identity":"worker","count":1},
{"location":"Sutherland","identity":"student","count":1},
{"location":"Sutherland","identity":"resident","count":2},
{"location":"Sutherland","identity":"worker","count":1},
{"location":"Miranda","identity":"resident","count":2},
{"location":"Miranda","identity":"worker","count":2},
{"location":"Miranda","identity":"student","count":1}
];
我发现这在 R 语言中非常容易实现,我会这样做:
long_array %>%
group_by(location, identity_long) %>%
summarise(n = n())
甚至只是
long_array %>%
count(location, identity_long)
但是我怎样才能在 javascript 中做到这一点?
我只想按两个属性对 JSON 对象进行分组,并计算相同的出现次数。
创建数组的一组唯一值并将其与 long_array
中存在的所有值进行比较,计算相同的值并将其保存在新数组中
const long_array = [{
"location": "Kirrawee",
"identity_long": "student"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "student"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Sutherland",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "student"
},
{
"location": "Miranda",
"identity_long": ""
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "resident"
}
];
const unique_values = [...new Map(long_array.map(obj => [JSON.stringify(obj), obj])).values()]; // this will remove duplicate values from long_array
const result = unique_values.map((val) => { // iterate in unique_values
let count = 0;
long_array.forEach((item) => {
item.location == val.location && item.identity_long === val.identity_long && count++
}); //iterate in long_array and count same values
return { ...val,
count: count
}
})
console.log(result);
解决此问题的一种方法是将对象转换为字符串(使用 JSON.stringify). Since comparing objects in JavaScript compares the instance, and not the fields/values, converting to strings would make it easier to identify duplicates. You could then do something like this 之类的方法来计算重复项!希望这会有所帮助!
您可以使用 lodash 等库并使用它的按功能分组来完成这种简单的方法,有点耗时的方法是实现您自己的按功能分组。
let long_array = [{
"location": "Kirrawee",
"identity_long": "student"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "student"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Sutherland",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "student"
},
{
"location": "Miranda",
"identity_long": ""
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "resident"
}
];
function addItemCounts(items, groupByKeys) {
var groups = _.groupBy(long_array, obj => {
return groupByKeys.map(key => obj[key]).join('-');
});
return _.map(groups, g => ({
...g[0],
count: g.length
}));
}
console.log(addItemCounts(long_array, ['identity_long', 'location']));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
这是一个解决方案,时间复杂度为 O(n*logn)
,space 复杂度为 O(n)
let long_array =
[
{ "location": "Kirrawee", "identity_long": "student" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "student" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Sutherland", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "student" },
{ "location": "Miranda", "identity_long": "" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "resident" }
];
// create map
let map = new Map()
for (let i = 0; i < long_array.length; i++) {
const s = JSON.stringify(long_array[i])
if (!map.has(s)) {
// if the map does not contain the object already
// i.e. its first occurrence
map.set(s, {
location: long_array[i].location,
identity: long_array[i].identity_long,
count: 1,
})
} else {
// if it no first occurrence
// increase the count straight way
map.get(s).count++
}
}
const result = Array.from(map.values())
console.log(result)
这是我设法想出的,现在我看到了其他答案,它非常复杂,但我认为它有效:
const grouped_and_counted_location_and_identity = [];
const sort = () => {
long_array.forEach((el) => {
let count = 0;
for (let i = long_array.indexOf(el); i < long_array.length; i++) {
if (
el.location == long_array[i].location &&
el.identity_long == long_array[i].identity_long
) {
count++;
}
}
el.count = count;
if (
!grouped_and_counted_location_and_identity.some(
(elem) =>
elem.location == el.location && elem.identity_long == el.identity_long
)
) {
grouped_and_counted_location_and_identity.push(el);
}
});
};
sort();
console.log(grouped_and_counted_location_and_identity);
根据包含 location
和 identity_long
的字符串创建计数映射:
{ Kirrawee-student: 1, ... }
映射每个条目并将其转换为所需的结构。
const long_array = [
{ "location": "Kirrawee", "identity_long": "student" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "student" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Sutherland", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "student" },
{ "location": "Miranda", "identity_long": "" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "resident" }
];
const countsMap = _.countBy(long_array, e => `${e.location}-${e.identity_long}`);
const longArrayWithCount = Object.entries(countsMap).map(([group, count]) => {
const [location, identity_long] = group.split('-');
return { location, identity_long, count };
});
console.log(longArrayWithCount);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>
这可以使用 Array.reduce
and Object.values
along with Optional Chaining
来实现
let long_array = [{location:'Kirrawee',identity_long:'student'},{location:'Kirrawee',identity_long:'visitor'},{location:'Kirrawee',identity_long:'visitor'},{location:'Kirrawee',identity_long:'worker'},{location:'Sutherland',identity_long:'student'},{location:'Sutherland',identity_long:'resident'},{location:'Sutherland',identity_long:'worker'},{location:'Sutherland',identity_long:'resident'},{location:'Miranda',identity_long:'resident'},{location:'Miranda',identity_long:'worker'},{location:'Miranda',identity_long:'student'},{location:'Miranda',identity_long:''},{location:'Miranda',identity_long:'worker'},{location:'Miranda',identity_long:'resident'}];
const formatData = (data) => {
const finalRes = data.reduce((res, {location, identity_long}) => {
//Formatting a composite key using `location` and `identity_long`
let key = `${location}_${identity_long}`;
res[key] = {
...res[key],
location,
identity: identity_long,
//Accessing the count form the result/accumulator if exists and if not assigning `0` and updating by 1
count: (res[key]?.count || 0) + 1
}
return res;
}, {});
//Finally returning the values of the object
return Object.values(finalRes)
}
console.log(formatData(long_array))
.as-console-wrapper {
max-height: 100% !important;
}
我有一个这样的 JSON 对象:
long_array =
[
{"location":"Kirrawee","identity_long":"student"},
{"location":"Kirrawee","identity_long":"visitor"},
{"location":"Kirrawee","identity_long":"visitor"},
{"location":"Kirrawee","identity_long":"worker"},
{"location":"Sutherland","identity_long":"student"},
{"location":"Sutherland","identity_long":"resident"},
{"location":"Sutherland","identity_long":"worker"},
{"location":"Sutherland","identity_long":"resident"},
{"location":"Miranda","identity_long":"resident"},
{"location":"Miranda","identity_long":"worker"},
{"location":"Miranda","identity_long":"student"},
{"location":"Miranda","identity_long":""},
{"location":"Miranda","identity_long":"worker"},
{"location":"Miranda","identity_long":"resident"}
];
我想要实现的是一个像下面这样的对象:
grouped_and_counted_location_and_identity =
[
{"location":"Kirrawee","identity":"student","count":1},
{"location":"Kirrawee","identity":"visitor","count":2},
{"location":"Kirrawee","identity":"worker","count":1},
{"location":"Sutherland","identity":"student","count":1},
{"location":"Sutherland","identity":"resident","count":2},
{"location":"Sutherland","identity":"worker","count":1},
{"location":"Miranda","identity":"resident","count":2},
{"location":"Miranda","identity":"worker","count":2},
{"location":"Miranda","identity":"student","count":1}
];
我发现这在 R 语言中非常容易实现,我会这样做:
long_array %>%
group_by(location, identity_long) %>%
summarise(n = n())
甚至只是
long_array %>%
count(location, identity_long)
但是我怎样才能在 javascript 中做到这一点?
我只想按两个属性对 JSON 对象进行分组,并计算相同的出现次数。
创建数组的一组唯一值并将其与 long_array
中存在的所有值进行比较,计算相同的值并将其保存在新数组中
const long_array = [{
"location": "Kirrawee",
"identity_long": "student"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "student"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Sutherland",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "student"
},
{
"location": "Miranda",
"identity_long": ""
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "resident"
}
];
const unique_values = [...new Map(long_array.map(obj => [JSON.stringify(obj), obj])).values()]; // this will remove duplicate values from long_array
const result = unique_values.map((val) => { // iterate in unique_values
let count = 0;
long_array.forEach((item) => {
item.location == val.location && item.identity_long === val.identity_long && count++
}); //iterate in long_array and count same values
return { ...val,
count: count
}
})
console.log(result);
解决此问题的一种方法是将对象转换为字符串(使用 JSON.stringify). Since comparing objects in JavaScript compares the instance, and not the fields/values, converting to strings would make it easier to identify duplicates. You could then do something like this 之类的方法来计算重复项!希望这会有所帮助!
您可以使用 lodash 等库并使用它的按功能分组来完成这种简单的方法,有点耗时的方法是实现您自己的按功能分组。
let long_array = [{
"location": "Kirrawee",
"identity_long": "student"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "visitor"
},
{
"location": "Kirrawee",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "student"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Sutherland",
"identity_long": "worker"
},
{
"location": "Sutherland",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "resident"
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "student"
},
{
"location": "Miranda",
"identity_long": ""
},
{
"location": "Miranda",
"identity_long": "worker"
},
{
"location": "Miranda",
"identity_long": "resident"
}
];
function addItemCounts(items, groupByKeys) {
var groups = _.groupBy(long_array, obj => {
return groupByKeys.map(key => obj[key]).join('-');
});
return _.map(groups, g => ({
...g[0],
count: g.length
}));
}
console.log(addItemCounts(long_array, ['identity_long', 'location']));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
这是一个解决方案,时间复杂度为 O(n*logn)
,space 复杂度为 O(n)
let long_array =
[
{ "location": "Kirrawee", "identity_long": "student" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "student" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Sutherland", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "student" },
{ "location": "Miranda", "identity_long": "" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "resident" }
];
// create map
let map = new Map()
for (let i = 0; i < long_array.length; i++) {
const s = JSON.stringify(long_array[i])
if (!map.has(s)) {
// if the map does not contain the object already
// i.e. its first occurrence
map.set(s, {
location: long_array[i].location,
identity: long_array[i].identity_long,
count: 1,
})
} else {
// if it no first occurrence
// increase the count straight way
map.get(s).count++
}
}
const result = Array.from(map.values())
console.log(result)
这是我设法想出的,现在我看到了其他答案,它非常复杂,但我认为它有效:
const grouped_and_counted_location_and_identity = [];
const sort = () => {
long_array.forEach((el) => {
let count = 0;
for (let i = long_array.indexOf(el); i < long_array.length; i++) {
if (
el.location == long_array[i].location &&
el.identity_long == long_array[i].identity_long
) {
count++;
}
}
el.count = count;
if (
!grouped_and_counted_location_and_identity.some(
(elem) =>
elem.location == el.location && elem.identity_long == el.identity_long
)
) {
grouped_and_counted_location_and_identity.push(el);
}
});
};
sort();
console.log(grouped_and_counted_location_and_identity);
根据包含
location
和identity_long
的字符串创建计数映射:{ Kirrawee-student: 1, ... }
映射每个条目并将其转换为所需的结构。
const long_array = [
{ "location": "Kirrawee", "identity_long": "student" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "visitor" },
{ "location": "Kirrawee", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "student" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Sutherland", "identity_long": "worker" },
{ "location": "Sutherland", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "resident" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "student" },
{ "location": "Miranda", "identity_long": "" },
{ "location": "Miranda", "identity_long": "worker" },
{ "location": "Miranda", "identity_long": "resident" }
];
const countsMap = _.countBy(long_array, e => `${e.location}-${e.identity_long}`);
const longArrayWithCount = Object.entries(countsMap).map(([group, count]) => {
const [location, identity_long] = group.split('-');
return { location, identity_long, count };
});
console.log(longArrayWithCount);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>
这可以使用 Array.reduce
and Object.values
along with Optional Chaining
let long_array = [{location:'Kirrawee',identity_long:'student'},{location:'Kirrawee',identity_long:'visitor'},{location:'Kirrawee',identity_long:'visitor'},{location:'Kirrawee',identity_long:'worker'},{location:'Sutherland',identity_long:'student'},{location:'Sutherland',identity_long:'resident'},{location:'Sutherland',identity_long:'worker'},{location:'Sutherland',identity_long:'resident'},{location:'Miranda',identity_long:'resident'},{location:'Miranda',identity_long:'worker'},{location:'Miranda',identity_long:'student'},{location:'Miranda',identity_long:''},{location:'Miranda',identity_long:'worker'},{location:'Miranda',identity_long:'resident'}];
const formatData = (data) => {
const finalRes = data.reduce((res, {location, identity_long}) => {
//Formatting a composite key using `location` and `identity_long`
let key = `${location}_${identity_long}`;
res[key] = {
...res[key],
location,
identity: identity_long,
//Accessing the count form the result/accumulator if exists and if not assigning `0` and updating by 1
count: (res[key]?.count || 0) + 1
}
return res;
}, {});
//Finally returning the values of the object
return Object.values(finalRes)
}
console.log(formatData(long_array))
.as-console-wrapper {
max-height: 100% !important;
}