jsPerf.app is an online JavaScript performance benchmark test runner & jsperf.com mirror. It is a complete rewrite in homage to the once excellent jsperf.com now with hopefully a more modern & maintainable codebase.
jsperf.com URLs are mirrored at the same path, e.g:
https://jsperf.com/negative-modulo/2
Can be accessed at:
https://jsperf.app/negative-modulo/2
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>// Lodash-based implementation that returns a Map with groupKey omitted
function groupByLodash(array) {
const grouped = _.groupBy(array, 'groupKey');
const map = new Map();
for (const [key, items] of Object.entries(grouped)) {
map.set(key, items.map(item => {
const { groupKey, ...rest } = item;
return rest;
}));
}
return map;
}
// Native implementation using Map with groupKey omitted
function groupByNativeMap(array) {
const map = new Map();
for (const item of array) {
const key = item.groupKey;
if (!map.has(key)) {
map.set(key, []);
}
const { groupKey, ...rest } = item;
map.get(key).push(rest);
}
return map;
}
// Optimized native implementation with pre-allocated arrays
function groupByNativeOptimized(array) {
const map = new Map();
// First pass: count items per group
const counts = new Map();
for (const item of array) {
const key = item.groupKey;
counts.set(key, (counts.get(key) || 0) + 1);
}
// Pre-allocate arrays with known sizes
for (const [key, count] of counts) {
map.set(key, []);
}
// Second pass: populate arrays
for (const item of array) {
const { groupKey, ...rest } = item;
map.get(groupKey).push(rest);
}
return map;
}
// Alternative using Object.create(null) for faster property access
function groupByNativeObject(array) {
const groups = Object.create(null);
for (const item of array) {
const key = item.groupKey;
if (!groups[key]) {
groups[key] = [];
}
const { groupKey, ...rest } = item;
groups[key].push(rest);
}
// Convert to Map
const map = new Map();
for (const key in groups) {
map.set(key, groups[key]);
}
return map;
}
// Alternative using Object.create(null) for faster property access
function createNativeObject(array) {
const groups = Object.create(null);
for (const item of array) {
const key = item.groupKey;
if (!groups[key]) {
groups[key] = [];
}
const { groupKey, ...rest } = item;
groups[key].push(rest);
}
return groups;
}
// Optimized version avoiding destructuring overhead
function groupByNativeNoDest(array) {
const map = new Map();
for (const item of array) {
const key = item.groupKey;
if (!map.has(key)) {
map.set(key, []);
}
// Manual property copying (avoiding destructuring overhead)
const newItem = {};
for (const prop in item) {
if (prop !== 'groupKey') {
newItem[prop] = item[prop];
}
}
map.get(key).push(newItem);
}
return map;
}
// Version using Object.assign
function groupByNativeAssign(array) {
const map = new Map();
for (const item of array) {
const key = item.groupKey;
if (!map.has(key)) {
map.set(key, []);
}
// Use Object.assign and delete
const newItem = Object.assign({}, item);
delete newItem.groupKey;
map.get(key).push(newItem);
}
return map;
}
// Generate test data: 50,000 groups with 1-4 objects each
function generateTestData() {
const data = [];
const numGroups = 50000;
for (let groupId = 0; groupId < numGroups; groupId++) {
// Each group has 1-4 items
const groupSize = Math.floor(Math.random() * 4) + 1;
const groupKey = `group_${groupId}`;
for (let j = 0; j < groupSize; j++) {
data.push({
groupKey: groupKey,
id: `${groupId}_${j}`,
name: `Item_${groupId}_${j}`,
value: Math.floor(Math.random() * 1000),
timestamp: Date.now() - (groupId * 1000),
category: `cat_${groupId % 100}`,
score: Math.random(),
active: Math.random() > 0.5
});
}
}
// Shuffle the array to simulate real-world unsorted data
for (let i = data.length - 1; i > 0; i--) {
const j = Math.floor(Math.random() * (i + 1));
[data[i], data[j]] = [data[j], data[i]];
}
return data;
}
// Generate the test data once
const testData = generateTestData();
console.log(`Generated ${testData.length} items across ~500,000 groups`);
// Smaller test set for quick validation
const smallTestData = testData.slice(0, 1000);Ready to run.
| Test | Ops/sec | |
|---|---|---|
| Lodash groupBy | | ready |
| Native with Map | | ready |
| Native map optimised | | ready |
| Objects with create(null) | | ready |
| Native Object -> Map | | ready |
| Native without destructuring | | ready |
| Native with Object.assign | | ready |
| Create just native object | | ready |
You can edit these tests or add more tests to this page by appending /edit to the URL.