4

I am trying to code FizzBuzz problem in Java8. It is working fine and I am getting desired output. For the number divisible by "3", it should return "Fizz", for the number divisible by "5", it should return "Buzz" and for the number divisible by both, it should return "FizzBuzz".

If I pass the value as "15" it returns:

["1","2","Fizz","4","Buzz","Fizz","7","8","Fizz","Buzz","11","Fizz","13","14","FizzBuzz"]

Now, I am stuck in one thing. I want to get an output as follows, if I pass the value as "15":

{"Fizz": [3, 6, 9, 12],"Buzz": [5, 10],"FizzBuzz": [15]}

I want to group the numbers by Fizz, Buzz, and FizzBuzz.

Here is my code:

public class FizzBuzzService {

private Map<Rule, String> ruleContainers = new HashMap();
private Set<Rule> rules  = new HashSet();

public FizzBuzzService(){
    addRule(i -> i % 15 == 0, "FizzBuzz");
    addRule(i -> i % 3 == 0, "Fizz");
    addRule(i -> i % 5 == 0, "Buzz");
}

public void addRule(Rule rule, String res) {
    rules.add(rule);
    ruleContainers.put(rule, res);
}

public String getValue(int i) {
    for (Rule rule : rules) {
        if (rule.apply(i)) {
            return ruleContainers.get(rule);
        }
    }
    return String.valueOf(i);
}

//then the origin code should be as follows:
public List<String> fizzBuzz(int n) {
     List<String> res = new ArrayList();
     for(int i = 1; i <= n; i++){
         res.add(getValue(i));
     }
     return res;
 }

interface Rule{
    boolean apply(int i);
}
}

I would really appreciate if someone can guide me. Thanks

Achal
  • 133
  • 2
  • 12
  • You could use a Map for res rather than a List. Where the key are the String "Fizz" etc.. and the values are the list of results – perkss Jan 20 '18 at 09:33

4 Answers4

3

Here's a way to do it with streams:

Map<String, List<String>> result = IntStream.rangeClosed(1, n)
    .filter(i -> i % 3 == 0 || i % 5 == 0)
    .boxed()
    .collect(Collectors.groupingBy(i ->
        i % 15 == 0 ? "FizzBuzz" :
        i %  3 == 0 ? "Fizz" : 
                      "Buzz"));

Without streams:

Map<String, List<String>> result = new HashMap<>();
for (int i = 0; i < n; i++) {
    if (i % 3 == 0 || i % 5 == 0) {
        String key = i % 15 == 0 ? "FizzBuzz" :
                     i %  3 == 0 ? "Fizz" : 
                                   "Buzz";
        result.computeIfAbsent(key, k -> new ArrayList<>()).add(i);
    }
}
fps
  • 33,623
  • 8
  • 55
  • 110
2

I would return a Map<String, List<Integer>> (use a LinkedHashMap instead of a HashMap and your keys will preserve insertion order) creating List<Integer>(s) for fizz, buzz and fizzbuzz values. It could be done in a single static method like,

public static Map<String, List<Integer>> fizzBuzz(int n) {
    Map<String, List<Integer>> map = new HashMap<>();
    List<Integer> fizz = new ArrayList<>(), //
            buzz = new ArrayList<>(), //
            fizzbuzz = new ArrayList<>();
    IntStream.rangeClosed(1, n).forEachOrdered(i -> {
        boolean f = i % 3 == 0, b = i % 5 == 0;
        if (f && b) {
            fizzbuzz.add(i);
        } else if (f) {
            fizz.add(i);
        } else if (b) {
            buzz.add(i);
        }
    });
    map.put("Fizz", fizz);
    map.put("Buzz", buzz);
    map.put("FizzBuzz", fizzbuzz);
    return map;
}
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
1
Map<String, List<Integer>> map = 
  IntStream.rangeClosed(1, 15)
    .mapToObj(n -> {
        String str = n % 15 == 0 ? "FizzBuzz" : 
            (n % 5 == 0) ? "Buzz" : (n % 3 == 0) ? "Fizz" : null;
        return str == null ? null : new SimpleEntry<>(str, n);
    }).filter(Objects::nonNull)
    .collect(groupingBy(Entry::getKey, mapping(Entry::getValue, toList())));

System.out.println(map);
sanit
  • 1,646
  • 1
  • 18
  • 21
0

Step 1: Apply rule on number.

Step 2: Find first matching rule.

Step 3: Filter elements which are not matched by any rule.

Step 4: Group by rule KeyWord

    public static void main(String[] args) {

        FizzBuzzService();
         Map<String, List<Integer>> fizzBuzzMap = IntStream.range(1, 31)
                  .mapToObj(number->applRule(number))
                  .filter(obj ->obj!=null)
                  .collect(Collectors.groupingBy(Entry::getKey,Collectors.mapping(Entry::getValue, Collectors.toList())));

         System.out.println("FizzBuzzMap : " + fizzBuzzMap);
    }

    private static SimpleEntry<String, Integer> applRule(Integer number) {

        Optional<Entry<Rule, String>> findFirst = ruleContainers.entrySet()
                                                                .stream()
                                                                .filter(rule -> rule.getKey().apply(number))
                                                                .findFirst();
        if (findFirst.isPresent()) {
            System.out.println("Number : " + number + " First Matching Rule : " +findFirst.get().getValue());
            return new SimpleEntry<>(findFirst.get().getValue(), number);
        }

        return null;
    }
}

    public static void FizzBuzzService(){
        addRule(i -> i % 3 == 0, "Fizz");
        addRule(i -> i % 5 == 0, "Buzz");
        addRule(i -> i % 15 == 0, "FizzBuzz");
    }

OutPut:

Number : 3 First Matching Rule : Fizz
Number : 5 First Matching Rule : Buzz
Number : 6 First Matching Rule : Fizz
Number : 9 First Matching Rule : Fizz
Number : 10 First Matching Rule : Buzz
Number : 12 First Matching Rule : Fizz
Number : 15 First Matching Rule : FizzBuzz
Number : 18 First Matching Rule : Fizz
Number : 20 First Matching Rule : Buzz
Number : 21 First Matching Rule : Fizz
Number : 24 First Matching Rule : Fizz
Number : 25 First Matching Rule : Buzz
Number : 27 First Matching Rule : Fizz
Number : 30 First Matching Rule : FizzBuzz
FizzBuzzMap : {FizzBuzz=[15, 30], Fizz=[3, 6, 9, 12, 18, 21, 24, 27], Buzz=[5, 10, 20, 25]}
Niraj Sonawane
  • 10,225
  • 10
  • 75
  • 104