We'll start with a function Distribute
. Its task is to distribute the artist
the specified number of times as evenly as possible. The passed in distribution
gives us the total amount of tracks, but it'll also inform us of which positions
have already been filled.
func Distribute(distribution []string, artist string, tracks int) []string {
stepSize := len(distribution) / tracks
index := 0
}
We'll now go into an "indefinite" loop that will run until we've distributed all
the tracks. As with all things recursive and indefinite, we'll start with the
termination condition:
func Distribute(distribution []string, artist string, tracks int) []string {
stepSize := len(distribution) / tracks
index := 0
for {
if tracks == 0 {
return distribution
}
}
}
Do not run this particular sample. When there are no more tracks we're done,
and return the (now updated) distribution. The implied mutation is a bit gross,
but Go does not have built-in persistent data structures, so we're going with
the flow.
Next, we'll add a clause that identifies a suitable position:
func Distribute(distribution []string, artist string, tracks int) []string {
stepSize := len(distribution) / tracks
index := 0
remainder := stepSize
for {
if tracks == 0 {
return distribution
}
if remainder == stepSize {
// Place track
}
// Prepare next iteration
}
}
With 5 tracks to distribute among 10 tracks, stepSize
will be 2
. We cannot
simply drop the one track every 2 indexes, as they might be occupied. So we use
the remainder
to flag when to position a track. The remainder
is initialized
to the stepSize
to force a track in the first available position.
func Distribute(distribution []string, artist string, tracks int) []string {
stepSize := len(distribution) / tracks
index := 0
remainder := stepSize
for {
if tracks == 0 {
return distribution
}
if remainder >= stepSize {
index = IndexOf(distribution, "", index)
distribution[index] = artist
remainder -= stepSize
tracks--
}
// Prepare next iteration
}
}
When the remainder is at least as much as the required step size, we find the
first available position from the current index. IndexOf
is a function I
wrote.
In Go, every type has a "nil type", and for strings this is ""
. This is why we
look up the first index in the distribution that holds an empty string, starting
at the desired index.
We then put the artist into the position we found, subtract the step size from
the remainder, and reduce the number of tracks to distribute. To prepare the
next iteration, we increment index
and remainder
:
func Distribute(distribution []string, artist string, tracks int) []string {
stepSize := len(distribution) / tracks
index := 0
remainder := stepSize
for {
if tracks == 0 {
return distribution
}
if remainder >= stepSize {
index = IndexOf(distribution, "", index)
distribution[index] = artist
remainder -= stepSize
tracks--
}
index++
remainder++
}
}
This will work so long as the stepSize
is an integer. Let's replace one of the
Nirvana tracks with something else, and see what happens:
- Nirvana: Smells Like Teen Spirit
- Nirvana: Come as You Are
- Nirvana: Heart-Shaped Box
- Nirvana: Something in the Way
- Pearl Jam: Once
- Pearl Jam: Daughter
- Leviathan: Dawn Vibration
- Leviathan: The Smoke of Their Torment
- Nile: Black Seeds of Vengeance
- Deathspell Omega: Abscission
Distributing this yields:
[]string{"Nirvana", "", "Nirvana", "", "Nirvana", "", "Nirvana", "", "", ""}
Oops! Not what we wanted. The problem is that we defined stepSize
as an int,
but it really should be 10/4 == 2.5
in this case. Two type conversions, and
we're all good:
func Distribute(distribution []string, artist string, tracks int) []string {
stepSize := float64(len(distribution)) / float64(tracks)
index := 0
remainder := stepSize
for {
if tracks == 0 {
return distribution
}
if remainder >= stepSize {
index = IndexOf(distribution, "", index)
distribution[index] = artist
remainder -= stepSize
tracks--
}
index++
remainder++
}
}
Which yields:
[]string{"Nirvana", "", "", "Nirvana", "", "Nirvana", "", "", "Nirvana", ""}
Voila!
We can call the function twice to distribute two artists:
distribution := DistributeSimple(make([]string, 10), "Nirvana", 4)
distribution = DistributeSimple(distribution, "Pearl Jam", 2)
//=>
[]string{
"Nirvana",
"Pearl Jam",
"",
"Nirvana",
"",
"Nirvana",
"Pearl Jam",
"",
"Nirvana",
"",
}
Looks good. However, note that the order of the calls matter:
distribution := DistributeSimple(make([]string, 10), "Pearl Jam", 2)
distribution = DistributeSimple(distribution, "Leviathan", 2)
distribution = DistributeSimple(distribution, "Nirvana", 4)
//=>
[]string{
"Pearl Jam",
"Leviathan",
"Nirvana",
"Nirvana",
"",
"Pearl Jam",
"Leviathan",
"Nirvana",
"",
"Nirvana",
}
If we distribute the artists with fewer tracks first, there might not be room
left to properly spread the ones with more tracks. We'll create another function
to distribute a whole list taking this into account.