0

I have a following list of teams with their score:

List<String> teams = Arrays.asList("Dortmund 8", "Bayern 10", "Madrid 9", "Bayern 2");

I want to convert it to map like Map<String, Integer>, so the String is the team and the score is Integer ("Dortmund", 8). Is it possible to make it without stream or better with stream? The example of converting will be much appreciated.

12
  • Is it possible that any given team could appear more than once with say a different score? If so how would you want to handle it? Ignore the first occurence, ignore the last occurence, or put all the scores in a list with the team as the key. Commented Feb 22, 2021 at 17:46
  • @WJS It is possible Commented Feb 22, 2021 at 17:48
  • See my updated question. Commented Feb 22, 2021 at 17:48
  • @WJS I thought that I will convert it to map and then it will be easier for me to count the overall score of each team Commented Feb 22, 2021 at 17:49
  • and when "Athletic Club" is inserted as club? Do you handle the spaces there as well? Commented Feb 22, 2021 at 17:50

4 Answers 4

5
for (String s : teams)
{ 
   String[] kv = s.split(" "); 
   int val = Integer.parseInt(kv[1]);

   map.put(kv[0], map.get(kv[0])==null ? val : map.get(kv[0]) + val);
}

//Map -  { Dortmund - 8 | Bayern - 12 | Madrid - 9 }

As an alternative to map.put, @Okx comment offers a cleaner approach to do this, using merge().

Internally, works like this for the HashMap implementation. If no value is found for the key, the value will be stored. If the key exists, it will apply the function passed as a third argument.

@Override
public V merge(K key, V value,
             BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
  if (value == null)
      throw new NullPointerException();
  if (remappingFunction == null)
      throw new NullPointerException();
  int hash = hash(key);
  Node<K,V>[] tab; Node<K,V> first; int n, i;
  int binCount = 0;
  TreeNode<K,V> t = null;
  Node<K,V> old = null;
  if (size > threshold || (tab = table) == null ||
      (n = tab.length) == 0)
      n = (tab = resize()).length;
  if ((first = tab[i = (n - 1) & hash]) != null) {
      if (first instanceof TreeNode)
          old = (t = (TreeNode<K,V>)first).getTreeNode(hash, key);
      else {
          Node<K,V> e = first; K k;
          do {
              if (e.hash == hash &&
                  ((k = e.key) == key || (key != null && key.equals(k)))) {
                  old = e;
                  break;
              }
              ++binCount;
          } while ((e = e.next) != null);
      }
  }
  if (old != null) {
      V v;
      if (old.value != null)
          v = remappingFunction.apply(old.value, value);
      else
          v = value;
      if (v != null) {
          old.value = v;
          afterNodeAccess(old);
      }
      else
          removeNode(hash, key, null, false, true);
      return v;
  }
  if (value != null) {
      if (t != null)
          t.putTreeVal(this, tab, hash, key, value);
      else {
          tab[i] = newNode(hash, key, value, first);
          if (binCount >= TREEIFY_THRESHOLD - 1)
              treeifyBin(tab, hash);
      }
      ++modCount;
      ++size;
      afterNodeInsertion(true);
  }
  return value;
}

   

So it will essentially follow the same logic, but with a cleaner syntax:

for (String s : teams)
{ 
   String[] kv = s.split(" "); 
   int val = Integer.parseInt(kv[1]);
   map.merge(kv[0], val, Integer::sum);
}

So appreciated, Okx, for sharing this.

Sign up to request clarification or add additional context in comments.

7 Comments

Is it possible to output the first team who first took the biggest score? For example if Bayern and Dortmund has 16, I want to output the team who had it first
but that would depend on the order you insert them into the original array, right?
also, that would depend as well if the collection implementation used sorts or not the entries....in order to know which one scored first, you should have a time metric. If not, you are lost in java specification ordering...
instead of map.put, map.merge(kv[0], val, Integer::sum) would be a simpler way of inserting into the map.
@Okx how would that work when trying to sum a null value? The first time the key is put
|
2

Ok, here are the three possibilities I mentioned. The do the following:

  • offer different ways to handle duplicates.
  • puts the values in a LinkedHashMap to preserve encounter order.
  • the non list version sorts them on the score, but keeping the identical scores in the same order. If you don't want them sorted, it is easily removed, or can be sorted on team names instead.
List<String> teams = Arrays.asList("Dortmund 8", "Bayern 10",
            "Dortmund 7", "Madrid 10", "Madrid 9");

This one keeps the first entry encountered for duplicate teams.

Map<String, Integer> map1 =
        teams.stream().map(str -> str.split("\\s+"))
                .sorted(Comparator.comparing(
                     arr -> Integer.parseInt(arr[1])))
                .collect(Collectors.toMap(arr -> arr[0],
                        arr -> Integer.parseInt(arr[1]),
                        (a, b) -> a, // <-- the 'a' indicates the first one
                        LinkedHashMap::new));
System.out.println("Keep the first score encountered");
map1.entrySet().forEach(System.out::println);
System.out.println();

This one keeps the last entry encountered for duplicate teams.

Map<String, Integer> map2 =
        teams.stream().map(str -> str.split("\\s+"))
                .sorted(Comparator.comparing(
                        arr -> Integer.parseInt(arr[1])))
                .collect(Collectors.toMap(arr -> arr[0],
                        arr -> Integer.parseInt(arr[1]),
                        (a, b) -> b,// <-- the 'b' indicates the last one
                        LinkedHashMap::new));

System.out.println("Keep the last score encountered");
map2.entrySet().forEach(System.out::println);
System.out.println();

And this one stores the scores in a list for each team.

Map<String, List<Integer>> map3 =
        teams.stream().map(str -> str.split("\\s+"))
                .collect(Collectors.groupingBy(arr -> arr[0],
                        LinkedHashMap::new,
                        Collectors.mapping(
                                a -> Integer.parseInt(a[1]),
                                Collectors.toList())));
System.out.println("Put the scores in a list");
map3.entrySet().forEach(System.out::println);

They print

Keep the first score encountered
Dortmund=7
Madrid=9
Bayern=10

Keep the last score encountered
Dortmund=8
Madrid=10
Bayern=10

Put the scores in a list
Dortmund=[8, 7]
Bayern=[10]
Madrid=[10, 9]

Comments

1
teams.stream().map(team -> team.split(" ")).collect(toMap(arr -> arr[0], arr -> Integer.parse(arr[1]))

3 Comments

Well, this basically works, but just not for cities with a space in it, for example Los Angeles.
Integer.parse ==> Integer.parseInt
But what about duplicates?
1
var pattern = Pattern.compile("(.+) (\\d+)");
teams
  .stream()
  .map(pattern::matcher)
  .filter(Matcher::matches)
  .collect(Collectors.groupingBy(
      m -> m.group(1), 
      Collectors.summingInt(m -> Integer.parseInt(m.group(2)))
  ));

assuming duplicates should have added scores
not sure if that is readable, eventually create a variable to hold the Collector.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.