PurelyFunctional.tv Newsletter 377: Three states of key-value pairs

Sign up for weekly Clojure tips, software design, and a Clojure coding challenge.

Issue 377 - May 11, 2020 · Archives · Subscribe

Clojure Tip 💡

Three states of key-value pairs

Sometimes I'm using a map and I want to both get the value for a key and check if the key exists in the map at the same time. In most cases, it would look something like this:

(if-some [v (get mp k)]
  (println "It's in there:" v) ;; v is never nil
  (println "It's not in there."))

That works most of the time. But sometimes, you might have and expect the value to be nil. It happens. In that case, you would confuse two separate cases, when it's in there but it's nil and when it's not in there at all.

In that case, I usually break out into a full on contains? check:

(if (contains? mp k)
  (let [v (get mp k)]
    (println "It's in there:" v)) ;; sometimes v is nil
  (println "It's not in there."))

That works. It separates out the three cases. Normally you wouldn't care, but notice that it's really got to do the map lookup twice. It's also got a let nested in the if. I didn't know this for the longest time, but there's actually a function in core that can do this in one step. Check this out.

(if-some [[_ v] (find mp k)]
  (println "It's in there:" v) ;; sometimes v is nil
  (println "It's not in there."))

The function that allows this is find. It looks up a key in a map, returns the map entry (key-value pair), or nil if the key is not found.

This function highlights that there are actually three cases. Sometimes we bundle the nil cases together (as in the first code example). Sometimes we bundle nil values in with non-nil values (as in the second and third code examples). Or we could treat all three cases specially. The thing is we have the choice---albeit it's implicit and not always clear from the code.

Quarantine update 😷

I know a lot of people are going through tougher times than I am. If you, for any reason, can't afford my courses, and you think the courses will help you, please hit reply and I will set you up. It's a small gesture I can make, but it might help.

I don't want to shame you or anybody that we should be using this time to work on our skills. The number one priority is your health and safety. I know I haven't been able to work very much, let alone learn some new skill. But if learning Clojure is important to you, and you can't afford it, just hit reply and I'll set you up. Keeping busy can keep us sane.

Stay healthy. Wash your hands. Stay at home. Wear a mask. Take care of loved ones.

Clojure Challenge 🤔

Last week's challenge

The challenge in Issue 376 was to find the distances to the nearest vowels in a string. You can see the submissions here.

You can leave comments on these submissions in the gist itself. Please leave comments! There are lots of great discussions in there. You can also hit the Subscribe button to keep abreast of the comments. We're all here to learn.

This week's challenge

Sudoku Validator

Write a function that validates a finished sudoku board. It should take a vector of vectors. The inner vectors represent rows from the sudoku board. The rows should contain nine integers. If the board is well-played, the function should return true, otherwise, false.

(sudoku-valid? [[ 1 5 2 4 8 9 3 7 6 ]
                [ 7 3 9 2 5 6 8 4 1 ]
                [ 4 6 8 3 7 1 2 9 5 ]
                [ 3 8 7 1 2 4 6 5 9 ]
                [ 5 9 1 7 6 3 4 2 8 ]
                [ 2 4 6 8 9 5 7 1 3 ]
                [ 9 1 4 6 3 7 5 8 2 ]
                [ 6 2 5 9 4 8 1 3 7 ]
                [ 8 7 3 5 1 2 9 6 4 ]]) ;=> true

(sudoku-valid? [[ 1 1 2 4 8 9 3 7 6 ]
                [ 7 3 9 2 5 6 8 4 1 ]
                [ 4 6 8 3 7 1 2 9 5 ]
                [ 3 8 7 1 2 4 6 5 9 ]
                [ 5 9 1 7 6 3 4 2 8 ]
                [ 2 4 6 8 9 5 7 1 3 ]
                [ 9 1 4 6 3 7 5 8 2 ]
                [ 6 2 5 9 4 8 1 3 7 ]
                [ 8 7 3 5 1 2 9 6 4 ]]) ;=> false

Notes:

  • A sudoku puzzle is successfully solved if all rows contain the numbers 1-9, all columns contain 1-9, and the nine 3x3 boxes contain 1-9. See the Wikipedia page for more information.

Thanks to this site for the challenge idea where it is considered Expert level in JavaScript.

You can also find these same instructions here. I might update them to correct errors and clarify the descriptions. That's also where submissions will be posted. And there's a great discussion!

As usual, please reply to this email and let me know what you tried. I'll collect them up and share them in the next issue. If you don't want me to share your submission, let me know.

Rock on!
Eric Normand