Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save psenger/38d2d6c7601b65fa92585abd1bc2a34a to your computer and use it in GitHub Desktop.

Select an option

Save psenger/38d2d6c7601b65fa92585abd1bc2a34a to your computer and use it in GitHub Desktop.
[Bucketize / Count Occurrences / Group / Separate an array of objects by a value as defined by the attribute, Ways to Sort] #JavaScript #Array #bucket #groupBy #collect #bag #counter
// Buckets of Data, with two Items appearing at the top of the list.
// note that OOO and AAA are at the top, whilst everything else is sorted
// Desired outcome:
// -------------------------------------------------------------------------------------------------
// OOO [ { type: 'OOO', attributes: {} }, { type: 'OOO', attributes: {} } ]
// AAA [ { type: 'AAA', attributes: {} } ]
// BBB [ { type: 'BBB', attributes: {} }, { type: 'BBB', attributes: {} } ]
// ZZZ [ { type: 'ZZZ', attributes: {} }, { type: 'ZZZ', attributes: {} } ]
const desiredFirstItem = 'OOO'
const desiredSecondItem = 'AAA'
const data = [
{
type: 'ZZZ',
attributes: {}
},
{
type: 'AAA',
attributes: {}
},
{
type: 'OOO',
attributes: {}
},
{
type: 'BBB',
attributes: {}
},
{
type: 'BBB',
attributes: {}
},
{
type: 'ZZZ',
attributes: {}
},
{
type: 'OOO',
attributes: {}
}
]
// This works, as long as there is not an `A` in the list. If there was, you might need to make
// a map entry that moved `A` : `AA` and so on.
const ALPHA = {
}
ALPHA[desiredFirstItem] = 'A'
ALPHA[desiredSecondItem] = 'B'
// create a Bucket / Map of type, values are array of objects
const groupedOldWay = data.reduce((acc, o) => {
acc[o.type] = acc[o.type] || []
acc[o.type] = [...acc[o.type], o]
return acc
}, {})
// Since 2025 / [EcmaScript 2026](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.groupby)
const grouped = Object.groupBy(data, obj => obj.type)
Object.entries(grouped /** groupedOldWay **/ )
.sort(([a],[b])=>{
// I want `OOO` and `AAA` to appear at the top of the list, in that order, and everything else,
// in alphabetical order
// I found this technique to work best.
// the positive 1 result indicates a change in order is needed.
// The negative -1 or zero result means no change needed.
return (ALPHA[a]?ALPHA[a]:a).localeCompare(ALPHA[b]?ALPHA[b]:b)
})
.map(([key, value]) => {
console.log(key, value)
return [key, value]
})
/**
* Count Occurrences of values in a list. Referred to as a Bag or HashBag
* Desired outcome:
* -------------------------------------------------------------------------------------------------
* const CountEmails = {
* 'READ': 1,
* 'UNREAD': 2,
* 'SPAM': 3,
* 'DRAFT': 0
* }
* ----
*/
const options = { flagA: false, flagB: true }
const values = [5, 5, 5, 2, 2, 2, 2, 2, 9, 4];
const countOccurrencesOldWay = (m) => m.reduce(function (acc, curr) {
acc[curr] = acc[curr] ? acc[curr] : 0
acc[curr]++
return acc
}, {})
// Since 2025 / [EcmaScript 2026](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.groupby)
const countOccurrences = (m) => {
const grouped = Object.groupBy(m, x => x);
return Object.fromEntries(
Object.entries(grouped).map(([key, array]) => [key, array.length])
);
}
const countOccurrencesOfObjectValues = (o) => countOccurrences(Object.values(o))
console.log( 'Occurrences Of Array =',JSON.stringify(countOccurrences(values) , null, 4 ) );
// Occurrences Of Array = {
// "2": 5,
// "4": 1,
// "5": 3,
// "9": 1
// }
console.log('Occurrences Of Object Values =',JSON.stringify(countOccurrencesOfObjectValues(options) , null, 4 ) );
// Occurrences Of Object Values = {
// "false": 1,
// "true": 1
// }
/**
* Bucketize / Group / Separate objects into buckets or groups, to categorize. Key is determined by the passed attribute value.
* Desired outcome:
* -------------------------------------------------------------------------------------------------
* {
* "bob": [
* {"id": 1, "name": "bob"}
* ],
* "larry": [
* {"id": 2, "name": "larry"},
* {"id": 6, "name": "larry"}
* ],
* "dan": [
* {"id": 3, "name": "dan"},
* {"id": 4, "name": "dan"}
* ],
* "tom": [
* {"id": 5, "name": "tom"}
* ],
* "undefined": [
* {"id": 7}
* ]
* }
* ----
*/
/**
* Bucket / Group objects in an array by the value of the passed attribute.
* @param {[*]} objects - array of objects
* @param {string} attributeName - the attribute name
* @deprecated since EcmaScript 2026
* @return {T}
*/
const bucketByAttributeOldWay = ( objects, attributeName ) => {
return (objects||[])
.reduce((previousValue,currentValue,currentIndex,array) => {
if( currentValue && previousValue.hasOwnProperty( currentValue[attributeName] ) ) {
previousValue[ currentValue[attributeName] ].push( currentValue );
} else if ( currentValue ) {
previousValue[ currentValue[attributeName] ] = [ currentValue ];
}
return previousValue
}, {});
}
//
/**
* Bucket / Group objects in an array by the value of the passed attribute.
* Since 2025 / [EcmaScript 2026](https://tc39.es/ecma262/multipage/fundamental-objects.html#sec-object.groupby)
* @param {[*]} objects - array of objects
* @param {string} attributeName - the attribute name
* @deprecated since EcmaScript 2026
* @return {T}
*/
const bucketByAttribute = (objects, attributeName) => {
return Object.groupBy(objects || [], obj => obj?.[attributeName]);
};
const data = [
{id: 1, name: 'bob'},
{id: 2, name: 'larry'},
{id: 3, name: 'dan'},
{id: 4, name: 'dan'},
{id: 5, name: 'tom'},
{id: 6, name: 'larry'},
{id: 7}
]
console.log(JSON.stringify(bucketByAttribute(data, 'name'), null, 4));
/**
{
"bob": [
{
"id": 1,
"name": "bob"
}
],
"larry": [
{
"id": 2,
"name": "larry"
},
{
"id": 6,
"name": "larry"
}
],
"dan": [
{
"id": 3,
"name": "dan"
},
{
"id": 4,
"name": "dan"
}
],
"tom": [
{
"id": 5,
"name": "tom"
}
],
"undefined": [
{
"id": 7
}
]
}
**/
const number = [99, 87, 3];
const letters = ['z', 'g', 'c'];
// Ascending order
// [ 3, 87, 99 ]
// [ 'c', 'g', 'z' ]
console.log(number.sort(function(a, b){return a - b}));
console.log(letters.sort(function(a, b){return a.localeCompare(b)}));
// Descending order / or you can call reverse on Ascending order
// [ 99, 87, 3 ]
// [ 'z', 'g', 'c' ]
console.log(number.sort(function(a, b){return b - a}));
console.log(letters.sort(function(a, b){return b.localeCompare(a)}));
// using the reverse() method
console.log(number.sort(function(a, b){return a - b}).reverse());
console.log(letters.sort(function(a, b){return a.localeCompare(b)}).reverse());
const strings = ['č','é','A','b','Đ'];
const defaultSort = Array.from(strings).sort();
const simpleSort = Array.from(strings).sort((a, b) => a - b);
const localeSort = Array.from(strings).sort((a, b) => {
return a.localCompare(b, 'en', { sensitivity: 'base' });
});
console.log(defaultSort);
console.log(simpleSort);
console.log(localeSort);
/**
// Pseudo Code
if (a is more than b) return 1
if (a is less than b) return -1
return 0
**/
number.sort( function( a , b){
if(a > b) return 1;
if(a < b) return -1;
return 0;
});
// [ 3, 87, 99 ]
console.log(numbers);
// this comparator works the same way (a, b) => a - b
// case insensitive sort
letters.sort(function (a, b) {
let x = a.toUpperCase(),
y = b.toUpperCase();
return x === y ? 0 : x > y ? 1 : -1;
});
// [ 'c', 'g', 'z' ]
console.log(letters);
// Sorting by date, where object x and y have hireDate
/**
let a = new Date(x.hireDate),
b = new Date(y.hireDate);
return a - b;
**/
// 0-Large Number.... then Negative Large number to small negative number
// put negative numbers at the end of the array
const arr = [12, 50, 6, -1, 0, 99, -100 -9]
const sorted = arr. sort ((a, b) => {
if (a < 0 && b < 0) { // if both b and a are negative, sort is normal
return a - b
} else if (a < 0 || b < 0) { // if one value is negative, sort in opposite direction
return b - a
} else {
return a - b // sort like normal positive first, negative second
}
}
)
console. log (sorted)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment