Saturday, April 6, 2024

Top 10 Java map() and flatMap() Functional Programming Coding Problems with Solutions

Hello friends, in last a couple of years, I have seen an increase in coding problems where interviewer expect you to solve using functional programming concepts and particularly map() and flatMap(). They do this because they want to know whether you know about these methods or not and whether you can use them in real world scenarios like solving coding problems. I have been collecting such coding problems and today, I am ready with 10 such map and flatMap related coding questions which I am going to share with you. The beauty of these coding interview questions is that you can easily solve them using Stream API and map() and flatMAp() method but if you try to solve them without using Stream API, it could be little tricky.  

While interviewer will not ask you to solve them in a particular way, this will be your chance to demonstrate that you know new Java tools and coding style to impress your interviewer. After all coding interviews are nothing but impressing your interviewer with your skills and attitude. 

Before solving these questions, I am assuming that you already know about Stream API in Java, particularly  map and flatMap() methods like how they work and when to use them, but if you are not, you can always go back and see my map tutorial and flatMap tutorial where I have shared real world examples.

To recap, map() can be used to transform one object to another, for example you can convert a String to Integer by applying parseInt() method and flatMap() does the same but it can also flatten the list. So you can use flatMap to convert one value to multiple values. 

For example, you can give a number to flatMap() and apply a method which return a List of numbers like the number itself and negative of it, and then you can flatten the return lists to generate a final list. This is an important trick to remember while solving coding problems using map and flatMap in Java

Now that we know enough about map() and flatMap() it's time to jump into questions

10 map and flatMap Coding Interview Questions and Programming exercises for Java developers 

Here is a list of popular questions I have collected form Java interviews which requires to be solved using functional style , I mean by using map and flatMap function in Java.  You can also combine these questions with my previous list of Stream and Functional programming interview questions where I shared theoretical and conceptual questions. 

1. Given a List of integers, return a List which contains square of those numbers. For example, input list is [1, 2, 3, 4, 5, 6] then you should return  a list containing [1, 4, 9, 16, 25, 36]

Solution: This is one of the simplest problem you will get on Java coding interviews. In this case all we need to do is use the map() function to transform each number to their square then collect the result using Collectors.toList() method. 

In order to do this transformation, we will use a simple square() function which can be easily represented by a lambda of x -> x * x as shown in below solution. 

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> squares = numbers.stream()
                               .map(x -> x * x)
                               .collect(Collectors.toList());

The key here is se used map() to convert number to their square and for that we passed a square function (a lambda x*x ) to the map() function.


2. Given a List of integers, return a List which contains numbers and negative of those numbers in same order. For example, input list is [1, 2, 3, 4, 5, 6] then you should return a list containing [1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6]

Solution: This is another popular question I have seen on Java coding interviews on investment banks like Citi, UBS, Barclays, Goldman Sachs etc. While this question look intimidating first, especially if you don't know about flatMap() and functional programming but all you need to do here is to use a flatMap() function to generate a bigger list. 

In this case, we have passed a lambda to flatMap() which returns a Stream of number and its negation, I mean Stream.of(x, -x) so this way we converted 1 to Stream.of(1, -1) and then flatMap() flatten all the stream returned by this method to create a big Stream containing (1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6) and we finally used Collectors.toList() to convert the Stream to List in Java. 

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
List<Integer> numbersAndNegatives = numbers.stream()
                                           .flatMap(x -> Stream.of(x, -x))
                                           .collect(Collectors.toList());

The key here is passing a function which can take one argument and return multiple values and then use flatMap() function to flatten the Stream and return a single Stream . 

Top 10 Java map() and flatMap() coding problems with Solutions




3. Given a List of Strings, return a List containing the lengths of those strings.
Input: ["apple", "banana", "orange", "kiwi"]
Output: [5, 6, 6, 4]

Solution: This is another simple coding question you can expect on coding interview, its so simple that sometime people even ask this on telephonic interviews.  All you need to do here is pass the String:: length() method to convert a String to integer

For example, by using String.length() you can convert "apple" to 5. Once again we have used Collectors.toList() to convert the Stream to List in Java. 

List<String> strings = Arrays.asList("apple", "banana", "orange", "kiwi");
List<Integer> lengths = strings.stream()
                               .map(String::length)
                               .collect(Collectors.toList());

If you also notice we have used method reference to call the length() method here. 


4. Given a List of Strings, return a List containing the individual characters of those strings.
Input: ["hello", "world"]
Output: ['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd']

Solution: In this question again if you notice, input List has two elements and output list has 10 elements which means we need to use flatMap() to flatten a List of List into a single List

Here is how you can solve this problem .
List<String> strings = Arrays.asList("hello", "world");
List<Character> characters = strings.stream()
                                   .flatMapToInt(CharSequence::chars)
                                   .mapToObj(ch -> (char) ch)
                                   .collect(Collectors.toList());

In this code, we first convert a List of Strings into a List of Characters. Then we flatten each String into individual characters and collects them into a new list.


5. Given a List of Lists of Integers, return a List containing all the elements of those Lists.
Input: [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
Output: [1, 2, 3, 4, 5, 6, 7, 8, 9]

Solution: This is the 5th question in this list of map and flatMap coding interview question and if you look closely , input contain List of List and output contains just a List, this means we are going to use flatMap() to flatten the list. 

Here is how you can solve this problem using flatMap():
List<List<Integer>> listOfLists = Arrays.asList(Arrays.asList(1, 2, 3), 
Arrays.asList(4, 5), Arrays.asList(6, 7, 8, 9));
List<Integer> allElements = listOfLists.stream()
                                       .flatMap(List::stream)
                                       .collect(Collectors.toList());

If you look closely, you will find that this code flattens a list of lists of integers into a single list of integers by combining the inner lists into one stream, you can see that we are passing List::stream to flatMap() and then collecting them into a new list.


6. Given a List of Strings, return a List containing the words that start with the letter 'a'.
Input: ["apple", "banana", "avocado", "kiwi", "apricot"]
Output: ["apple", "avocado", "apricot"]

Solution: This question is bit different from previous questions because here we also need to use filter() method as we need to return a List containing the words that start with the letter 'a'. In fact, you can solve this question without using map() or flatMap() because we are not transforming input. You can see that input list also contains "apple" and output list also contains "apple"

Here is how you can solve this problem:
List<String> words = Arrays.asList("apple", "banana", "avocado", "kiwi", "apricot");
List<String> aWords = words.stream()
                           .filter(word -> word.startsWith("a"))
                           .collect(Collectors.toList());

The only thing to notice here is use of filter() method which accept a Predicate functional interface. This interface takes an input (code) and return true or false. Here the code is whether word startsWith "a" or not. 


7. Given a List of Lists of Strings, return a List containing all the words from all the Lists?
Input: [["apple", "banana"], ["orange", "kiwi"], ["grape", "pear"]]
Output: ["apple", "banana", "orange", "kiwi", "grape", "pear"]

Solution: This question is similar to the 5th question in the list, only difference is here we have array of String instead of array of integers previously. Again we need to use flatMap() because we need to flatten 3 lists into one. 

Here is the code to solve this problem

List<List<String>> listOfLists = Arrays.asList(Arrays.asList("apple", "banana"),
                Arrays.asList("orange", "kiwi"), Arrays.asList("grape", "pear"));
List<String> allWords = listOfLists.stream()
                                  .flatMap(List::stream)
                                  .collect(Collectors.toList());

Only thing to notice here is List::stream which convert a List to Stream. We have also used method reference here to call the stream() method of List class. 


8. Given a List of Lists of Integers, return a List containing the square of each number in each List?
Input: [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
Output: [1, 4, 9, 16, 25, 36, 49, 64, 81]

Solution: Now this coding question is bit more interesting because it require you to use both map() and flatMap() in single pipeline. For example, not only we need to flatten the 3 lists into one but also calculate the square of each number. 

This means we can use flatMap() for flattening the List and map() for transforming number into their square.

Here is the code:
List<List<Integer>> listOfLists = Arrays.asList(Arrays.asList(1, 2, 3), 
                        Arrays.asList(4, 5), Arrays.asList(6, 7, 8, 9));
List<Integer> squares = listOfLists.stream()
                                   .flatMap(list -> list.stream().map(x -> x * x))
                                   .collect(Collectors.toList());

This code flattens nested lists, squares each integer, and collects them into a single list. The key here is when to use map() method, we need to apply map() to each number on individual list. 

9. Given a List of Strings representing sentences, return a List containing all the distinct words from all the sentences?
Input: ["Hello world", "The sun is shining", "Hello everyone"]
Output: ["Hello", "world", "The", "sun", "is", "shining", "everyone"]

Solution: This question is bit more lengthy than all the questions we have seen so far. It not only require you to flatten the list because input list  contains 3 elements and output list contains 7, but also require you to split the sentence into word and then find the unique words to include in final list. 

In order to filter duplicates we can use distinct() method of Stream class as shown in below code:

List<String> sentences = Arrays.asList("Hello world", "The sun is shining",
                                        "Hello everyone");
List<String> distinctWords = sentences.stream()
                              .flatMap(sentence -> Arrays.stream(sentence.split("\\s+")))
                                     .distinct()
                                     .collect(Collectors.toList());

This code takes a list of sentences and converts them into a stream of words by splitting each sentence into words. WE are using split() method of String with regular expression "\\s+" to split the String. It then ensures that only distinct words are retained in the stream. Finally, it collects the distinct words into a new list.

Sometimes, interviewer also ask to solve this problem without using Stream API, in that case you can use the old way to solve the problem as shown below:

    public static List<String> distinctWords(List<String> sentences) {
        // Create a HashSet to store distinct words
        Set<String> uniqueWords = new HashSet<>();

        // Iterate through each sentence
        for (String sentence : sentences) {
            // Split the sentence into words using whitespace as delimiter
            String[] words = sentence.split("\\s+");
            
            // Iterate through each word in the sentence
            for (String word : words) {
                // Add each word to the HashSet
                uniqueWords.add(word);
            }
        }

        // Convert the HashSet to a List
        List<String> distinctWordsList = new ArrayList<>(uniqueWords);
        return distinctWordsList;
    }

This function ensures that each word is added only once to the HashSet, resulting in a list of distinct words. Finally, the HashSet is converted back to a List and returned as the result.

9. Given a List of Lists of Strings representing sentences, return a List containing all the distinct words from all the sentences.
Input: [["Hello world"], ["The sun is shining"], ["Hello everyone"]]
Output: ["Hello", "world", "The", "sun", "is", "shining", "everyone"]

Solution: This questions is quite similar to previous question but instead of a single level list, we have a nested structure where each inner list represents a sentence, and the outer list contains multiple such lists. 
The objective is to extract all the distinct words from all the sentences across all the inner lists.
List<List<String>> listOfSentences = Arrays.asList(
    Arrays.asList("Hello world"),
    Arrays.asList("The sun is shining"),
    Arrays.asList("Hello everyone")
);
List<String> distinctWords = listOfSentences.stream()
                                            .flatMap(List::stream)
                       .flatMap(sentence -> Arrays.stream(sentence.split("\\s+")))
                                            .distinct()
                                            .collect(Collectors.toList());

In order to solve the problem, we have first flatten the list structure using flatMap() to obtain a stream of individual sentences. You can see that we have passed List::stream method which return a Stream of given List. 

Then, each sentence is split into words using split("\\s+"), and flatMap() is used again to flatten the stream of arrays of words into a single stream. Finally, distinct() is applied to get the distinct words, and collect(Collectors.toList()) is used to collect them into a List.

That's all about 10 Java coding interview questions you can solve using map() and flatMap() methods. These solutions demonstrate the power and flexibility of map() and flatMap() in transforming and flattening data structures in Java streams which every Java developer should be familiar of. Most of these problems require using flatMap() to flatten the nested lists and then map() to transform input values. You can also use these coding interview questions as programming exercises to practice and improve your knowledge on map() and flatMap() methods. 

Other Java Interview Questions you may like:

Let me know how do you like this article and if you have any other question to share, feel free to do in the comments. I would appreciate if everyone reading this article till the end leave me a comment or feedback. Thanks a lot. 

1 comment :

Anonymous said...

Awesome questions Javin, thanks a lot. In fact, I have seen a couple of questions form this list already on previous Java interviews, I think its the first and second ones.

Post a Comment