Присоединяйтесь к отфильтрованным значениям карты

У меня есть статическая карта:

private static final Map<String, String> queries;
static {
    Map<String, String> tmpMap = new HashMap<>();
    tmpMap.put("USER_WITHOUT_ROLE_QUERY","...");
    tmpMap.put("PROFIL_WITHOUT_ROLE_QUERY","...");
    //Removed code for readability 
    queries = Collections.unmodifiableMap(tmpMap);
}

И два массива String, как показано ниже:

  • String[] typeFilter может содержать комбинацию этих значений: ["WITHOUT_PROFIL", "WITHOUT_ROLE", "WITHOUT_USER"];
  • String[] elementFilter может содержать комбинацию этих значений: ["PROFIL", «РОЛЬ», «УТИЛИСАТОР»];

Я хочу в зависимости от значений в typeFilter и elementFilter отфильтровать карту queries и объединить значения отфильтрованной карты с помощью " UNION ".

Например, если typeFilter содержит "WITHOUT_ROLE", а elementFilter содержит "PROFIL" и "USER", отфильтрованная карта будет содержать только записи PROFIL_WITHOUT_ROLE_QUERY и USER_WITHOUT_ROLE_QUERY, поэтому результат будет: queries.get("PROFIL_WITHOUT_ROLE_QUERY") + " UNION " + queries.get("USER_WITHOUT_ROLE_QUERY").

Вот что я пробовал:

// Filter queries map
List<String> queriesMapKeys = new ArrayList<>();
for (String element : elementFilter) {
    for (String type : typeFilter) {
        queriesMapKeys.add(queries.get(element + "_" + type + "_QUERY"));
    }
}
String[] queriesToJoin = queries.entrySet().stream().filter(e -> queriesMapKeys.contains(e.getKey())).collect(// I'm stuck here :-/);

String sqlQuery = String.join(" UNION ", queriesToJoin);

Как я могу это решить? И есть ли лучший способ выполнить приведенный выше код?

Кажется, вы просто хотите получить значения для набора ключей, я правильно понимаю?

lexicore 03.04.2018 17:04

вы ищете String [] queryToJoin = запросы .... .collect (Collectors.toList ()). toArray (new String [0]);

SEY_91 03.04.2018 17:07

@lexicore да, действительно, и было бы неплохо, если бы была возможность объединить первый вложенный цикл с кодом, в котором я фильтрую карту.

Renaud is Not Bill Gates 03.04.2018 17:12

Я думаю, что это нормально, что вы предварительно вычисляете имена запросов, которые хотите фильтровать. Но лучше использовать для этого Set как HashSet.

lexicore 03.04.2018 17:20

@ SEY_91, на самом деле есть только stream.toArray(String[]::new).

M. Prokhorov 03.04.2018 17:22

у тебя есть ответ @IchigoKurosaki?

Adesh Kumar 04.04.2018 11:35
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
6
77
5

Ответы 5

Вы можете попробовать следующее.

List<String> queriesToJoin = queries.entrySet().stream().filter(e -> queriesMapKeys.contains(e.getKey())).collect(Collectors.toList());

String sqlQuery = String.join(" UNION ", queriesToJoin);

queriesToJoin в этом случае идентификатор типа List<Map.Entry<String,String>>, а не List<String>

Renaud is Not Bill Gates 03.04.2018 17:13

Вам не обязательно использовать потоки. Вы можете просто использовать класс StringJoiner:

import java.util.StringJoiner;

<...>
StringJoiner builder = new StringJoiner(" UNION ");
for (String element : elementFilter) {
  for (String type : typeFilter) {
    String key = element + "_" + type + "_QUERY";
    if (queries.containsKey(key)) {
      builder.add(queries.get(key));
    }
  }
}

String sqlQuery = builder.toString();

Интересный факт: класс StringJoiner используется внутренне, когда вы выполняете String::join или собираете данные с Stream с помощью Collectors.joining.

Кажется, вы просто хотите получить значения для набора ключей из вашей карты. Это будет что-то вроде:

 List<String> queriesToJoin = queries
     .entrySet()
     .stream()
     .filter(e -> queriesMapKeys.contains(e.getKey()))
     .map(Map.Entry::getValue)
     .collect(Collectors.toList());

Вы также просите лучше реализовать это.

Вы используете форматированную строку для моделирования определенных свойств ваших запросов и фильтров. Вместо этого вы можете попробовать использовать для этого классы или предикаты.

Например, вместо использования строк в качестве ключей / имен запросов рассмотрите возможность использования перечислений.

 public enum QueryId {
     USER_WITHOUT_ROLE_QUERY(USER, WITHOUT_ROLE),
     PROFIL_WITHOUT_ROLE_QUERY(PROFIL, WITHOUT_ROLE);
     QueryId(TargetElement element, QueryType type) {...}
     public TargetElement getElement() {...}
     public QueryType getQueryType() {...}
 }

Затем вы можете фильтровать с помощью предикатов, которые используют QueryId.getElement() или QueryId.getQueryType().

Попробуйте это, если вы хотите объединить первый вложенный цикл с кодом, в котором вы фильтруете карту.

String[] queriesToJoin =  Arrays.asList(elementFilter)
        .stream()
        .flatMap(f -> Arrays.asList(typeFilter)
                .stream()
                .map(s -> f + "_" + s + "_QUERY"))
        .filter(t -> queries.containsKey(t))
        .map(t -> queries.get(t))
        .collect(Collectors.toList()).toArray(new String [0]);

В вашем коде

// Filter queries map
List<String> queriesMapKeys = new ArrayList<>();
for (String element : elementFilter) {
    for (String type : typeFilter) {
        queriesMapKeys.add(queries.get(element + "_" + type + "_QUERY"));
    }
}

вы уже запрашиваете карту и добавляете результат get в список queriesMapKeys. Если ключа нет на карте, ссылки null появятся в списке. Ключи отсутствуют в этом списке. Таким образом, выполняя другую операцию потока, пытаясь сопоставить список с ключами карты, такими как

String[] queriesToJoin = queries.entrySet().stream()
    .filter(e -> queriesMapKeys.contains(e.getKey())) …

вообще не может работать.

Но в любом случае в этом не было бы необходимости. Вам нужно только обработать потенциальные значения null. Либо:

queriesMapKeys.removeIf(Objects::isNull);
String sqlQuery = String.join(" UNION ", queriesMapKeys);

или же

String sqlQuery = queriesMapKeys.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.joining(" UNION "));

Последний может быть объединен с предыдущей потоковой операцией, заменяя вложенные циклы, создавая queriesMapKeys:

String sqlQuery = Arrays.stream(elementFilter)
    .flatMap(element -> Arrays.stream(typeFilter)
        .map(type -> queries.get(element + "_" + type + "_QUERY")))                
    .filter(Objects::nonNull)
    .collect(Collectors.joining(" UNION "));

Другие вопросы по теме