NiceFeed: Updating all subscriptions at once (And, picking up Python)

Per my earlier post, I had a nice break last week from working on NiceFeed while my internet was unusable. However, the plan to stay away from my computer turned out to be a miserable failure, and I decided instead to learn Python. I used Python Projects for Beginners: A Ten-Week Bootcamp Approach to Python Programming by Connor Milliken except instead instead of 10 weeks, I gave myself one. Of course, the real test is what to do with it now—I’d been planning to learn Django and pick up some backend skills, but the chapter on introductory data analytics really got my wheels spinning… We’ll see.

Anyway. A NiceFeed user emailed me about possibly adding an option to update all subscriptions at once. Up to this point, the closest thing I had was a background worker (via WorkManager) that cycles through all existing subscriptions but only updates one subscription every 15 minutes. If it finds any new content, it posts a notification displaying the most recent entry found. Needless to say, if a user has many, many subscriptions, it would take a long time for a full cycle to complete.

We want the app to be able to update all subscriptions in one move. This means quickly looping through every one of them, each time requesting the subscription URL, identifying any new content, and saving it to the database. To do this, I created a new CoroutineWorker class that does the following:


private val repo = NiceFeedRepository.get()
private val feedParser = FeedParser(repo.networkMonitor)

override suspend fun doWork(): Result {
    val feedUrls = repo.getFeedUrlsSynchronously()
    if (feedUrls.isEmpty()) return Result.success()

    for (url in feedUrls) {
        val currentEntryIds: List<String> = repo.getEntryIdsByFeedSynchronously(url)
        val feedWithEntries: FeedWithEntries? = feedParser.getFeedSynchronously(url)

        feedWithEntries?.entries?.let { entries ->
            val newEntries = entries.filterNot { currentEntryIds.contains(it.url) }
            val entryIds = entries.map { it.url }
            val oldEntryIds = currentEntryIds.filterNot { entryIds.contains(it) }
            repo.handleBackgroundUpdate(url, newEntries, oldEntryIds)
        }
    }

    return Result.success()
}

From there, it’s simply a matter of feeding it to WorkManager, which I’ve set to run the worker once a day. I’ve also added the option (via Snackbar message) to run the worker whenever new subscriptions are imported by OPML, since OPML only imports minimal feed data, without any entries. Now we are that much closer to a standard feature of most respectable RSS readers. I find that I enjoy “background” code like this much more than designing and creating the user interface… Hence, I’m still undecided as to whether I should also add a button to allow the option of running the worker whenever the user wants, rather than just on schedule. Thoughts for another day.

FizzBuzz, part 2: A fancier way

Piggybacking off my earlier post, because I have an obsessive personality I couldn’t help thinking of fancier, if more convoluted, ways to write FizzBuzz. I didn’t like the fact that I had to write the modulo operation and “Fizz” and “Buzz” more than once each. I came up with many variations of the code below (once again, in Kotlin):

val map = mapOf(3 to "Fizz", 5 to "Buzz")

fun main() {
    for (i in 1..100) {
        val fb = StringBuilder()
        map.forEach { if (i % it.key == 0) fb.append(it.value) }
        println(if (fb.isEmpty()) i else fb)
    }
}

Here the code uses a map (a set of key-value pairs) to establish the required relationships: 3 to Fizz, and 5 to Buzz. The outer loop, counting 1 through 100, is the same as before. We initialize an empty StringBuilder, and then start a second, inner loop that goes through each of the two aforementioned key-value pairs in the map and checks it against the current number i. If the operation returns true (that is, i is a multiple of the key of the current pair in the map), the value of the pair is added to the StringBuilder. So, if the operation returns true for the first key-value pair, we get “Fizz.” If only the second, “Buzz.” If both times, we get “FizzBuzz.” And each of these words appear in the code only once. Finally, we check if the string is empty in the end, and if so we just print the current number.

I could just as easily have done string concatenation by first assigning an empty string to a mutable variable (var fb = ""), but for reasons I don’t yet entirely understand, StringBuilder performs better, especially when loops are involved. In a program this small though, nobody will notice… I think?

Another beauty of this approach is that if we wanted to, we can change the rules very easily. We can add a third key-value pair to the map, maybe 7 to “Fuzz,” or use a different map entirely without rewriting anything else. Okay, I’m done with FizzBuzz now, moving on.

FizzBuzz: An intermezzo

Scaring myself with the revelation that many would-be developers ostensibly cannot write FizzBuzz, I became anxious to know if I would pass the test. The task is to write a program in as few characters as possible that will print the numbers 1 to 100 each on a new line, except multiples of 3 become “Fizz,” multiples of 5 are “Buzz,” and multiples of both 3 and 5 are “FizzBuzz.” As it turns out, the solution is nice and short in Kotlin:

fun main() {
    for (i in 1..100) { 
        println(when {
            i % 3 == 0 && i % 5 == 0 -> "FizzBuzz"
            i % 3 == 0 -> "Fizz"
            i % 5 == 0 -> "Buzz"
            else -> i
        })
    }
}

The overall logic is simple enough, just a plain old loop through 1 to 100 and conditionals to determine what to print. But I had to think for a second to remember the modulus operator… Here it means that when i is divided by either 3 or 5 and the remainder is 0, then it is a multiple of 3 or 5. I wonder if there’s an even more concise way to do this.

The when conditional makes things neat, instead of nested if/else if statements. i % 3 == 0 && i % 5 == 0 needs to come first, or else the program will print “Fizz” as long as i is a multiple of 3, regardless of whether it is also a multiple of 5. And so on. Another thing is that if i weren’t pressed for characters, I would maybe write "$i" in that else branch instead of just i to be consistent with the preceding branches, which all result in strings. But this appears to be a non-issue.