Reputation: 6987
There has got to be a more expressive way to do this using map
, filter
, reduce
and friends.
Background:
The categories
property is an [Category]?
, where each element has a videos
property of type [Video]
.
I'm trying to find the index path for video
.
var indexPath: NSIndexPath?
for var i = 0; i < categories?.count ; ++i {
for var j = 0; j < categories?[i].videos.count ; ++j {
if categories?[i].videos[j] == video {
indexPath = NSIndexPath(forRow: j, inSection: i)
}
}
}
if let indexPath = indexPath {
tableView.reloadRowsAtIndexPaths([ indexPath ], withRowAnimation: UITableViewRowAnimation.Automatic)
}
Upvotes: 2
Views: 361
Reputation: 539775
A possible solution (written in Swift 2):
let indexPaths = categories?.enumerate().flatMap() { (section, aCategory) in
aCategory.videos.enumerate().filter() { (_, aVideo) in
aVideo == video
}.map { (row, _) in
NSIndexPath(forRow: row, inSection: section)
}
} ?? []
indexPaths
is an array of all matching index paths (or an empty array). If you don't need/like that, use
let indexPath = categories?.enumerate().flatMap() { (section, aCategory) in
aCategory.videos.enumerate().filter() { (_, aVideo) in
aVideo == video
}.map { (row, _) in
NSIndexPath(forRow: row, inSection: section)
}
}.first
instead, which is an optional index path.
categories?.enumerate()
is a sequence of (section, aCategory)
pairs. For each category, aCategory.videos.enumerate()
is a
sequence of (row, aVideo)
pairs. This sequence is filtered
according to the search term, and the filtered pairs are mapped
to an index path.
So the result of the transform passed to flatMap()
is an array
of index paths of the matching items, one array for each category.
flatMap()
joins these to a single array.
In Swift 1.2 this would be
let indexPaths = flatMap(enumerate(categories ?? [])) { (section, aCategory) in
filter(enumerate(aCategory.videos)) { (_, aVideo) in
aVideo == video
}.map { (row, _) in
NSIndexPath(forRow: row, inSection: section)
}
}
Upvotes: 3
Reputation: 31782
This isn't the most functional, nor the most concise, but it seems pretty readable to me. Assumes the video to be found is unique.
var location: NSIndexPath? = nil
for (section, category) in enumerate(categories ?? []) {
if let row = find(category.videos, video) {
location = NSIndexPath(forRow: row, inSection: section)
break
}
}
Upvotes: 1
Reputation: 427
If you do this rarely, make a generator that returns a tuple of index paths and videos then filter on video equaling the video you need
If you make an enumeration generator, it mainly moves the loop into it's own object. categoryIndex and videoIndex become vars in the generator, and next basically does what the 2nd/3rd clauses of the for loop statement do
Right now, you're effectively working with a dictionary of index paths to videos.
If you're doing this lookup a lot and changing the data in the collection rarely, maintaining a bidirectional dictionary (video -> IP) and (IP->video) may be faster, then it's a quick operation to find the item. The generator is a way to create that data structure, but honestly, you've gotta involve a profiler if performance becomes an issue there
Upvotes: 0