Reputation: 51
I'm trying to populate TableView with sections. Where an artist is the Section Name and all songs of that artist are listed below. In total over 100 artists. Like so
FirstArtist
- Song 1
- Song 2
SecondArtist
- Song 1
- Song 2
OneMoreArtist...
I have an array of objects Song
struct Song {
let songName: String?
let album: String?
let artist: String?
}
var songs = [Song]()
And an array of all artists names
var artists = [String]()
As I understand, to populate tableView with sections I need a Dictionary with Artist as key and array of Songs as value, like so
var toShow = [String : [Song]]()
So, then I'm trying to loop
for artist in artists {
for song in songs {
if song.artist == artist {
toShow[artist] = [song]
// toShow[artist]?.append(song)
}
}
}
But it doesn't work.
Probably I'm going wrong way.
What is the solution in this situation?
Thanks
Update
Made a stuct
struct ArtistWithSongs {
let name: String
let songs: [Song]
init(name: String, songs: [Song]) {
self.name = name
self.songs = songs
}
}
and trying to loop
var artistWithSongs = [ArtistWithSongs]()
for artist in artists {
for song in songs {
if artist == song.artist {
artistWithSongs.append(ArtistWithSongs(name: song, songs: [song]))
}
}
}
But apparently, my variant of looping is not correct. Now I'm getting an array of objects contains duplicate keys and only one Song per Key. It looks like
[Atrist1 : [Song1], Artist1 : [Song2], Artist1 : [Song3], ...]
My question is - What is the right way to make a loop, or is it possible somehow to merge objects with an identical key inside a Dictionary to get this [Artist1 :[Song1, Song2, Song3]]
?
Upvotes: 1
Views: 73
Reputation: 51
Thanks to everyone, I found a solution here, it worked for what I was trying to achieve. Roughly like this
let toShow = artistWithSongs.reduce([ArtistWithSongs](), { partialResult, artist in
var dupe = partialResult.filter {$0.songName == group.name }.first
if let dupeArtist = dupe {
dupeArtist.songs?.append(contentsOf: artist.songs ?? [])
return partialResult
} else {
var newPartialResult = partialResult
newPartialResult.append(group)
return newPartialResult
}
})
Upvotes: 2
Reputation: 2920
If you want to transform an array of songs ([Song]
) to a dictionary ([String : [Song]]
), I would recommend using functional style:
self.toShow = songs.reduce([String: [Song]]()) { acc, next in
// mutable copy
var acc = acc
// since artist is optional we need to substitute it with non-optional value if it's nil
let artist = next.artist ?? "Unknown Artist"
// if dictionary already has this artist, then append the `next` song to an array
if acc[artist] != nil {
acc[artist]?.append(next)
} else {
// otherwise create new array
acc[artist] = [next]
}
return acc
}
In your table's data source provide the number of sections:
func numberOfSections(in tableView: UITableView) -> Int {
return self.toShow.keys.count
}
And then you'll face a trouble: for number of rows in section you'll need to know which artist is the section stands for. So, I suggest you to use one more structure, an array of artists so that you keep the order of sections consistent:
self.artists = Array(self.toShow.keys).sorted()
This way, you can provide your table with following numbers of sections and rows:
func numberOfSections(in tableView: UITableView) -> Int {
return self.artists.count
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
let artist = self.artists[section] // get the artist's name
return self.toShow[artist]?.count ?? 0
}
Upvotes: 1
Reputation: 100503
In addition to your Song
struct
struct Song {
let songName: String?
let album: String?
}
create Artist
one like
struct Artist {
let name:String
let songs = [Song]()
}
Then create an array of them
var artists = [Artist]()
let s1 = Song(songName:"name1",album:"alb1")
let s2 = Song(songName:"name2",album:"alb2")
let artist1 = Artist(name:"art",songs:[s1,s2])
artists.append(artist1)
In numberOfSections
return
artists.count
In numberOfRows
return
artists[section].songs.count
In cellForRowAt
access
let song = artists[indexPath.section].songs[indexPath.row]
Upvotes: 1