@Udf(description = "When transforming a map, "
      + "two functions must be provided. "
      + "For each map entry, the first function provided will "
      + "be applied to the key and the second one applied to the value. "
      + "Each function must have two arguments. "
      + "The two arguments for each function are in order: the key and then the value. "
      + "The transformed map is returned."
  )
  public <K,V,R,T> Map<R,T> transformMap(
      @UdfParameter(description = "The map") final Map<K, V> map,
      @UdfParameter(description = "The key lambda function") final BiFunction<K, V, R> biFunction1,
      @UdfParameter(description = "The value lambda function") final BiFunction<K, V, T> biFunction2
  ) {
    if (map == null || biFunction1 == null || biFunction2 == null) {
      return null;
    }

    return map.entrySet()
        .stream()
        .collect(Collectors.toMap(
            entry -> biFunction1.apply(entry.getKey(), entry.getValue()),
            entry -> biFunction2.apply(entry.getKey(), entry.getValue())));
  }
@Test
  public void shouldReturnTransformedMap() {
    final Map<Integer, Integer> map1 = new HashMap<>();
    assertThat(udf.transformMap(map1, biFunction1(), biFunction2()), is(Collections.emptyMap()));
    map1.put(3, 100);
    map1.put(1, -2);
    assertThat(udf.transformMap(map1, biFunction1(), biFunction2()), is(Stream.of(new Object[][] {
        { -97, 97 },
        { 3, -3 },
    }).collect(Collectors.toMap(data -> (Integer) data[0], data -> (Integer) data[1]))));

    final Map<String, String> map2 = new HashMap<>();
    assertThat(udf.transformMap(map2, biFunction3(), biFunction4()), is(Collections.emptyMap()));
    map2.put("123", "456789");
    map2.put("hello", "hi");
    assertThat(udf.transformMap(map2, biFunction3(), biFunction4()), is(Stream.of(new Object[][] {
        { "456789123", false },
        { "hihello", true },
    }).collect(Collectors.toMap(data -> (String) data[0], data -> (Boolean) data[1]))));
  }