Dele opp array i grupper ved bruk av typescript/javascript
Jeg har dette:
products = [
{
'id': 1
'name: 'test'
},
{
'id': 2
'name: 'test'
},
{
'id': 3
'name: 'test'
}
... etc, etc
]
Jeg trenger å omstrukturere det til dette:
products = [
row1: [
{
'id': 1
'name: 'test'
},
{
'id': 2
'name: 'test'
},
{
'id': 3
'name: 'test'
},
{
'id': 4
'name: 'test'
}
]
row2: [
{
'id': 5
'name: 'test'
},
{
'id': 6
'name: 'test'
},
{
'id': 7
'name: 'test'
},
{
'id': 8
'name: 'test'
}
]
row3: [.. etc, etc
]
Det vanskelige er at antallet objekter i hver gruppe settes ved hjelp av en variabel (i dette eksemplet ville variabelen være 4).
Hvordan kan jeg oppnå dette ved å bruke Typescript/Javascript? Det gjør meg gal!
Takk
Svar #1
Det du egentlig ber om er chunking, i motsetning til gruppering. Noen av de andre svarene prøver å bruke Object.groupBy() eller Array.prototype.reduce() for å behandle hvert element og gruppere dem, noe som er ineffektivt. Chunking, i motsetning til gruppering, tar ingen hensyn til innholdet i elementene; derfor trenger vi ikke å gå gjennom (og behandle) hvert enkelt element i arrayen.
Alternativ A: Kombinert inndelings-/oversettelsesmetode
Det raskeste alternativet er en enkel løkke som går gjennom arrayet med trinn på chunk-størrelse, og skjærer ut delmengder mens vi går:
function chunkAndTranslate(array, chunkSize) {
// Create a plain object for housing our named properties: row1, row2, ...rowN
const output = {},
// Cache array.length
arrayLength = array.length;
// Loop variables
let arrayIndex = 0, chunkOrdinal = 1;
// Loop over chunks
while (arrayIndex < arrayLength) {
// Use slice() to get a chunk. Note the incrementing/assignment operations.
output[`row${chunkOrdinal++}`] = array.slice(arrayIndex, arrayIndex += chunkSize);
}
return output;
}
// Testing with a simplified demo array
console.table(chunkAndTranslate([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 4));<script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script><style>.as-console-wrapper{display:block}</style><script>console.config({timeStamps:false,maximize:true})</script>Dette har noen fordeler over forslagene reduce() og Object.groupBy(). Denne:
- Steg etter størrelsen på bitene i stedet for 1; så det utfører langt færre iterasjoner.
- Har ikke behov for gjentatt, komparativ logikk eller beregninger (ingen divisjon eller avrunding nedover) for hver indeks. Innholdet i elementet er irrelevant; derfor bryr vi oss ikke om å behandle hvert enkelt element.
- Definerer hver
rowN-egenskap én gang og trenger aldri å sjekke om den allerede eksisterer. - Bruker den native
Array.prototype.slice()-metoden for å velge våre delsett i stedet for å legge til individuelle elementer ett om gangen og gjentatte ganger endre størrelsen på chunk-arrayet.
Alternativ B: Del opp først, deretter reduser
Alternativt kan vi separere oppdelings- og oversettelsestrinnene. Vi vil lage en mer gjenbrukbar Array.prototype.chunk()-metode, slik det er vanlig i andre språk og biblioteker*. Vi vil deretter bruke Array.prototype.reduce() på den resulterende, kortere, todimensjonale arrayen. Dette reduserer mange av svakhetene ved å bruke reduce alene og blir faktisk raskere enn Alternativ A ved visse terskler for lengden på input-arrayen og chunk-størrelsen:
if (!Array.prototype.hasOwnProperty("chunk")) {
Object.defineProperty(Array.prototype, "chunk", {
enumerable: false,
value: function(size) {
// Cache array.length
const length = this.length;
// Pre-size output array so we don't have to push/resize
const output = new Array(Math.ceil(length / size));
// Loop variables
let seekIndex = 0,
outputIndex = 0;
// Loop over chunks
while (seekIndex < length) {
// Use slice() to get a chunk. Note the incrementing/assignment operations.
output[outputIndex++] = this.slice(seekIndex, seekIndex += size);
}
// Return our chunks
return output;
}
});
}
console.table(
// Testing with a simplified demo array
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// pre-chunk
.chunk(4)
// Reduce to translate into the desired object
.reduce((output, chunk, index) => {
output[`row${index + 1}`] = chunk;
return output;
}, {})
);<script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script><style>.as-console-wrapper{display:block}</style><script>console.config({timeStamps:false,maximize:true})</script>* Du vil normalt implementere en chunking-metode som dette som en generator, siden chunking vanligvis brukes til å behandle store datasett, og derfor kanskje du ikke ønsker å materialisere et helt nytt array som inneholder alle chunks. For enkelhets skyld, og fordi generatoren medfører ekstra overhead, har vi laget et enkelt output-array med chunks. Når det er sagt, her er en generator-implementasjon hvis du vil ta en titt. Når Iterator.prototype.reduce() og lignende metoder får bred støtte, vil generatorer være enda mer attraktive.
if (!Array.prototype.hasOwnProperty("chunk")) {
Object.defineProperty(Array.prototype, "chunk", {
enumerable: false,
value: function*(size) {
// Cache array.length
const length = this.length;
// Loop variable
let seekIndex = 0;
// Loop over chunks
while (seekIndex < length) {
// Use slice() to yield a chunk. Note the incrementing/assignment operations.
yield this.slice(seekIndex, seekIndex += size);
}
}
});
}
console.table(
// Testing with a simplified demo array
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
// pre-chunk with our generator
.chunk(4)
// Iterator.prototype.reduce() to translate into the desired object
// check browser support for this function
.reduce((output, chunk, index) => {
output[`row${index + 1}`] = chunk;
return output;
}, {})
);<script src="https://gh-canon.github.io/stack-snippet-console/console.min.js"></script><style>.as-console-wrapper{display:block}</style><script>console.config({timeStamps:false,maximize:true})</script>Her er en jsbench, som bruker en chunk-størrelse på 25 og en inngangslengde på 99, som sammenligner alternativ A og B sammen med løsninger gitt av andre svar. Det aksepterte reduce-svaret presterer faktisk dårligst. Det er tydelige ytelsesstraffer for å behandle individuelle elementer når grupperingen ikke tar hensyn til innholdet i elementene:
| Løsning | Operasjoner/sek (høyere er bedre) | I forhold til raskest |
|---|---|---|
| Alternativ A: mens + skive | 1 300 000 | raskest |
| Alternativ B: dele opp → redusere | 1 100 000 | 21 % tregere |
| Objekt.grupperEtter | 71 000 | 95 % tregere |
| redusere | 57 000 | 96 % tregere |
Føl deg fri til å forkere testen for å prøve forskjellige inndata eller batche størrelser.
Svar #2
Bruk Array.reduce()
Du kan kjøre en .reduce()-metode på produktlisten din, slik:
var products = [
{ 'id': 1, name: 'test' },
{ 'id': 2, name: 'test' },
{ 'id': 3, name: 'test' },
{ 'id': 4, name: 'test' },
{ 'id': 5, name: 'test' },
{ 'id': 6, name: 'test' }
]
var numberOfObjects = 4 // <-- decides number of objects in each group
var groupedProducts = products.reduce((acc, elem, index) => {
var rowNum = Math.floor(index/numberOfObjects) + 1
acc[`row${rowNum}`] = acc[`row${rowNum}`] || []
acc[`row${rowNum}`].push(elem)
return acc
}, {})
console.log(groupedProducts)