The Question
In addition to all of my blog posts about personal finance, I thought it might be interesting and valuable to start a more technical series of posts geared toward those of my readers who are software developers. I’m going to kick this concept off by answering a question I got in an actual technical interview: given an array of letters in the alphabet, which can appear multiple times, return a hash of letter counts sorted alphabetically. For example, the following array:
["s", "a", "b", "b", "h", "d", "e", "f", "o", "q", "r", "w", "s", "c", "u", "g", "a", "h", "h", "r"]
should return the following hash of key-value pairs:
{"a"=>2, "b"=>2, "c"=>1, "d"=>1, "e"=>1, "f"=>1, "g"=>1, "h"=>3, "o"=>1, "q"=>1, "r"=>2, "s"=>2, "u"=>1, "w"=>1}
There are several reasons I decided to start here: first, I failed the interview, possibly because it was my first time coding on a whiteboard and coding on a whiteboard is, well, terrible—ask any developer, they’ll tell you. Second, this interview question could theoretically pop up for my more-technical readers, so this could be a good resource for them. Third, I wound up using a solution to this problem in a real web application several times at my job at Radial Development Group (AFTER the interview, of course), which means it is, at the very least, a practical question.
The question was given at a fintech/payment processing company, which, as you may guess, means that they work in the Java programming language. Possibly because they were aware I’m unfamiliar with Java, they told me I could write the answer in any language I want (I chose my first and favorite language, Ruby). They also told me that their stack uses JavaScript (which has nothing to do with Java, non-coders) pretty frequently.
So I decided to answer the question in all three languages for this blog post. Let’s start with Ruby.
My Answer in Ruby
First and foremost, I defined a sample array that we’re going to be manipulating:
letter_array = ["s", "a", "b", "b", "h", "d", "e", "f", "o", "q", "r", "w", "s", "c", "u", "g", "a", "h", "h", "r"]
Pretty simple, right? lettersarray[0] will return ‘s’, lettersarray[1] returns ‘a’, and so on. Ruby lets you use single or double quotes interchangeably (‘‘ vs ““), so this array could just as easily have single quotes around each character. The only significant difference is that double quotes allow string interpolation. It’s an urban legend that single quotes are more performant in Ruby.
I have some duplicates to make sure that my compiler actually counts, so the key ‘h’ returns 3, not 1.
Now we need to define a Ruby method that accepts the array, generates a hash, sorts it, then returns the sorted hash (line numbers added for reference):
1 def count_and_sort_letters array
2 letter_counts = {}
3 array.each_with_object(letter_counts) do |letter, letter_counts|
4 if letter_counts[letter]
5 letter_counts[letter] += 1
6 else
7 letter_counts[letter] = 1
8 end
9 end
10 letter_counts.sort_by{|k, v| k}.to_h
11 end
Then call the method with the letter_array:
count_and_sort_letters letter_array
Great! So what am I doing here? Let’s break it down:
Line 1 defines a Ruby method, which I named
count_and_sort_letters
. It accepts an argument,array
, which we’ll be manipulating throughout the rest of the method. The method is closed on line 11 withend
.Line 2 defines an empty hash,
letter_counts
. I could inline this into line 3 by passingeach_with_object
an empty hash rather thanletter_counts
, but I only wanted tosort_by
once, since sort operations are generally expensive. Therefore, I needed the variable declared outside of theeach
block.Line 3 begins the Ruby
each_with_object
block. It accepts an enumerable as an argument (an array or a hash, generally, and in this case my empty object,letter_counts
). It then passes two arguments into the block: the current element in the enumerable (whichever letter we’re on inletter_counts
), and the object we passed in.We don’t want to return values of 0 in the hash; that is,
”p” => 0
should not be returned. So on line 4 I check whetherletter_counts[letter]
is truthy or not (in other words, it checks whether that key has already been given a value, becausenil
is falsey). This means the first iteration is checking whetherletter_counts[“s”]
exists or not, and returns false. On the fourth iteration, however,letter_counts[“b”]
DOES exist, so it executes line 5, incrementingletter_counts[“b”]
by 1.In the instance that the key isn’t already assigned in the
letter_counts
hash, theelse
statement beginning on line 6 is executed, creating that key with a value of 1 in the hash. So in the first iteration,letter_counts[“p”]
gets the value1
.After the
each_with_object
loop finished, I callsort_by
on the hash on line 10. I pass it a one-line block (in Ruby,{ }
is the one-liner equivalent todo … end).
It accepts the key and value of each element in the hash,k
andv
, and is told to sort usingk
rather thanv
(the letter, not the count). Since Ruby’ssort_by
returns an array, I then callto_h
to convert it back to a hash instead. Ruby methods return their last line unless you tell them to do otherwise, so I don’t need areturn
statement here.
The result is what we wanted: {"a"=>2, "b"=>2, "c"=>1, "d"=>1, "e"=>1, "f"=>1, "g"=>1, "h"=>3, "o"=>1, "q"=>1, "r"=>2, "s"=>2, "u"=>1, "w"=>1}
Some notes:
Ruby acts like Ruby a lot in this example: it returns more or less what you want and doesn’t throw a lot of errors without much effort on my part, but it then needs to be told to sort properly and return a hash.
Like many Ruby developers, I sometimes forget where Ruby ends and Rails begins; I tried calling
.present?
onletter_counts[letter]
and kept getting an error, before finally remembering that.present?
is from ActiveSupport in Rails, not Ruby. Myif
statement works fine without it in this instance, but it wouldn’t act quite so gracefully if you pass innil
in an effort to trip it up.
My Answer in JavaScript
In my experience writing JavaScript in an actual app, developers tend to use libraries like Lodash to get many of the helpful functions other languages take for granted (simple sorting and type checking functionality, for example). For my JavaScript example, I didn’t do this, but I did use ES6 syntax to make for slightly simpler reading.
Again, I defined the array we’re testing with:
const letterArray = ["s", "a", "b", "b", "h", "d", "e", "f", "o", "q", "r", "w", "s", "c", "u", "g", "a", "h", "h", "r"];
Then I wrote an arrow function that counts, then sorts, letterArray
, before returning the counted and sorted object.
1 const countedAndSortedLetters = (array) => {
2 const unsortedCounts = {};
3 array.forEach((letter) => {
4 if (unsortedCounts[letter]) {
5 unsortedCounts[letter] += 1;
6 } else {
7 unsortedCounts[letter] = 1;
8 }
9 })
10 const sortedCounts = {};
11 Object.keys(unsortedCounts).sort().forEach((key) => {
12 sortedCounts[key] = unsortedCounts[key];
13 })
14 return sortedCounts;
15 }
I then call the arrow function to return the value in my console with:
console.log(countedAndSortedLetters(letterArray));
And get the result:
{ a: 2, b: 2, c: 1, d: 1, e: 1, f: 1, g: 1, h: 3, o: 1, q: 1, r: 2, s: 2, u: 1, w: 1 }
Let’s break this one down:
I set a constant,
countedAndSortedLetters
, to the value returned by a function on line 1. I pass inarray
as an argument again, and declare an empty object,unsortedCounts
, on line 2. Line 1’sconst countedAndSortedLetters = (array) => {
syntax looks funky, but is just a slightly-shorter syntax thanconst countedAndSortedLetters = function(array) {
in this case. At Radial, we frequently work with React, meaning arrow functions’ lexical binding of thethis
keyword makes our code easier to read and understand—we don’t need theconst
keyword inside our React classes, and we don’t need to call.bind(this)
all the time.I would normally use _.forEach or something similar because it tends to handle a lot of errors and JavaScript gotchas gracefully, but instead used JavaScript’s built-in
forEach
to iterate through the passed-in array on line 3.I personally like using JavaScript’s bracket notation over dot notation for objects because 1. it reminds me of Ruby hashes, and 2. it makes it slightly clear that you’re referencing a value on an object, not calling a function (so I’m not hunting for the
()
at the end of the function name). Lines 4-7 are more or less the same logic as in Ruby: if the key has a value, increment it by 1; otherwise, set it to 1.JavaScript doesn’t have a great way to sort, or even map, objects (again, Lodash or a similar library is usually very handy here), so instead I define
sortedCounts
on line 10 as an empty object.I then get the keys from
unsortedCounts
as an array,sort()
them, andforEach()
one more time on line 11. This is a place where ES6 syntax really shines, by the way;.forEach((key) => {})
is very clear syntactically about what it is you’re doing.Line 12 iterates over the sorted keys and sets that key’s value in
sortedCounts
.Line 14 tells the overall arrow function to return
sortedCounts
, which then assigns that value to ourcountedAndSortedLetters const.
Some notes:
I was really feeling the struggle of JavaScript without any helper libraries.
node_modules
is a monster, but it’s also very helpful.I’ve never liked JavaScript’s syntax for prototype functions, like
Object.keys
. It feels counterintuitive when I’m writing the code to pass a function onObject
an instance of itself.It’s more obvious here than in the Ruby example that I’m looping twice—as such, it’s probably also less performant than Ruby’s
sort_by
, if I had to guess. I may consider revising these to see if I can only loop through once to make a more-performant solution.
My Answer in Java
I used repl.it’s online Java compiler for this, which had me name the file (and therefore the class) Main
in this example.
1 import java.util.HashMap;
2
3 public class Main{
4 public static void main(String []args){
5 String[] letterArray = new String[]{"s", "a", "b", "b", "h", "d", "e", "f", "o", "q", "r", "w", "s", "c", "u", "g", "a", "h", "h", "r"};
6 letterCounter(letterArray);
7 }
8
9 private static void letterCounter(String[] letterArray) {
10 HashMap<String, Integer> letterCounts = new HashMap<>();
11
12 for(int i=0; i < letterArray.length; i++) {
13 String letter = letterArray[i];
14 if (letterCounts.containsKey(letter)) {
15 letterCounts.put(letter, letterCounts.get(letter) + 1);
16 } else {
17 letterCounts.put(letter, 1);
18 }
19 }
20 System.out.println(letterCounts);
21 }
22 }
The response is very similar to the JavaScript response:
{a=2, b=2, c=1, d=1, e=1, f=1, g=1, h=3, o=1, q=1, r=2, s=2, u=1, w=1}
Here’s what’s happening this time:
I import
HashMap
on line 1. It’s a Java utility, but throws an error when not imported, so I figure it counts as being part of Java’s main implementation. It gives me the hash structure I’ve been using previously.Java throws an error if your class isn’t named what your file is named, so line 3 is a class called
Main
. I think I’ll just let Java be Java here and move on (Repl is great, but it also didn’t seem interested in letting me rename the only file).public static void main(String []args){
on line 4 is, as far as I can tell from Oracle’s documentation, pretty standard boilerplate for declaring a new class.public
means other classes can call this one;static
is sort of likeself
in Ruby in that it’s saying it’s defining a class method, not an instance method for that class;void
clarifies that this method does not return a value; andmain
is the method name, so we’re definingMain.main
.(String []args)
means that one can pass several strings into the method when called. Interestingly,Main.main
throws an error if it returns anything, so this seems to essentially be a good place to call other methods, not a good place to define the main thing we plan to do.Line 5 defines
letterArray
, as I’ve been doing.String[]
means I’m defining an array of strings, not just one string. I found it confusing that arrays have curly braces{}
around them coming from Ruby and JavaScript.Line 6 calls a method called
letterCounter
with myletterArray
argument.Line 9 defines the
letterCounter
private method.private
here appears to achieve the same results as defining methods below theprivate
keyword in Ruby. It’s a class method, doesn’t return anything (returnsvoid
), is calledletterCounter
, and accepts an array of strings calledletterArray
.Line 10 continues the trend of declaring everything in Java. In order to make a hash named
letterCounts
, I need to say that I’m making aHashMap
withString
keys andInteger
values, and callnew HashMap()
to make it an emptyHashMap
instance.I used a
for
loop on line 12, mostly because it was obvious when reading the documentation about it that JavaScript must have pulled this syntax straight from Java. Nice to have one familiar part, at least. As Java and JavaScript devs will doubtlessly know, the syntax here is defining an integer, which is incremented every time you loop through the array until it becomes equal to the array’s size. This is one convenience of having arrays start at index 0 rather than 1, you can say< array.length
rather than<= array.length
.Line 13 was initially
String letter = String.valueOf(letterArray[i])
because I had an array ofchars
rather thanStrings
which Java refuses to convert into strings unless I specifically tell it to.Lines 14 through 18, again, are pretty much the same logic I’ve used before: to update the
letterCounts
value ati
dependent upon whether it already has a value or not. It was interesting (not good or bad, just odd to my eyes) that Java’s HashMaps use.put
and.get
to set and get values. My brain immediately connected it to the HTTP verbsPUT
andGET
, which are similar but not the same.Line 20 outputs the resultant
letterCounts
hash.
Some notes:
Java is very, very strict from my perspective, even stricter than JavaScript, which already feels strict to me coming from Ruby. If you don’t tell it exactly what you mean, it will fail, hard.
Errors in Java are wonderfully explicit, even to someone as new to the language as I am. In spite of being unfamiliar with the syntax, I was rarely confused about why my code failed. Even when the error thrown confused me (I’d try returning something the method didn’t expect me to return, for example), Java is like Ruby and JavaScript in that it has good documentation from both the maintainers (Oracle, in this case) and the community (a word which here means “Stack Overflow”).
There’s a lot of boilerplate in Java. I’m pretty spoiled by Ruby.
Java is the only language of the three that gave me exactly what it was I wanted once I played by its rules: the values are sorted all on their own, meaning I didn’t need to loop through the code again, meaning this is probably the most performant of the three letter-sorting methods.
Java feels like Ruby’s father who isn’t on speaking terms with Ruby but is mostly cordial at Thanksgiving. It also feels like JavaScript’s estranged stepfather from JavaScript’s mom’s third marriage. I noticed several object-oriented patterns I’m familiar with (and like) from Ruby, but also some syntactical choices that I presume JavaScript went with because it was how Java did it.
Well, this turned out to be a lot more work than I set out to do. But I hope this was informative for how to pull in a bunch of random, unsorted data, put it in a hash, sort it, and pass it along to the next part of your app—I’ve found this very helpful in Rails apps when pushing information over to a Webpack/JavaScript front end.
What improvements would you make to my implementations? Let me know in the comments!
Post number 60.