Дедупликация повторяющихся чисел в Java - PullRequest
0 голосов
/ 26 сентября 2019

У меня есть TreeMap из <Double,Double>.Я пытаюсь уменьшить карту для всех последовательных дублированных значений.
Т.е. ключ, значения

 (1.0, 1.0)
 (2.0, 1.0)
 (3.0, 1.0)
 (4.0, 1.0)
 (5.0, 2.0)
 (6.0, 2.0)
 (7.0, 2.0)
 (8.0, 1.0)
 (9.0, 1.0)
(10.0, 1.0)

уменьшены до

 (1.0, 1.0)
 (4.0, 1.0)
 (5.0, 2.0)
 (7.0, 2.0)
 (8.0, 1.0)
(10.0, 1.0)

Я могу получить уникальные значения с помощью

List<Double> uniqueValues = test.values().parallelStream().distinct()
    .collect(Collectors.toList());

И я могуитерируйте эти значения, чтобы получить ключи к значениям

List<Integer> uniqueKeys = test.entrySet().stream()
    .filter(entry -> Objects.equals(entry.getValue(), uniqueValue))
    .map(Map.Entry::getKey)
    .collect(Collectors.toList());

Но теперь я теряю начальные и конечные точки каждого набора дублированных значений.

Что такоехорошее решение для этого?Я хотел получить предоставленные ключи, но проблемы возникают в приведенном выше примере, где повторное число возвращается.

Ответы [ 3 ]

1 голос
/ 27 сентября 2019

Вы можете собрать каждую серию в отдельный список.Благодаря LinkedList у вас есть легкий доступ к последнему элементу, и вы можете проверить, не изменилось ли оно.Если значение изменяется, то создается новая LinkedList для сбора следующих записей.

LinkedList<LinkedList<Map.Entry<Double,Double>>> linkedLists = new LinkedList<>();

test.entrySet().stream().forEach(e -> {
    if (linkedLists.isEmpty() || 
        ! linkedLists.getLast().getLast().getValue().equals(e.getValue())) {
        linkedLists.add(new LinkedList<>());
    }
    linkedLists.getLast().add(e);
});

System.out.println(linkedLists);

После этого вы можете изменить это на окончательный список

System.out.println(linkedLists.stream()
    .flatMap(ll -> Arrays.asList(ll.getFirst(), ll.getLast()).stream())
    .collect(Collectors.toList()));

или карту с сохраненным порядком

System.out.println(linkedLists.stream()
    .flatMap(ll -> Arrays.asList(ll.getFirst(), ll.getLast()).stream())
    .collect( Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (a1, a2) -> a1, () -> new LinkedHashMap<>())));

0 голосов
/ 27 сентября 2019

Сначала я скажу, что вы не должны использовать Double для ключа на карте.Подробнее здесь: Удвоение в HashMap

Далее приведен пример с Map<Integer, Integer> для упрощения логики.Вам нужно будет адаптировать его для Map<Double, Double>

Логика заключается в том, что первая и последняя записи карты всегда будут в карте результатов.Так что вам просто нужно отфильтровать те, что в середине (индекс 1 к размеру карты -1).Просто пропустите те, которые имеют то же значение, что и предыдущий или следующий

Для версии цикла

// get the sorted list of keys
List<Integer> keys = new ArrayList<>(map.keySet());
Collections.sort(keys);

List<Integer> resultKeys = new ArrayList<>();
// first key will always be in the result map, add it
resultKeys.add(keys.get(0));
// for each following key, add if the value is different from both previous or next
for (int i = 1; i < keys.size()-1; i++) {
    Integer key = keys.get(i);
    Integer value = map.get(key);

    Integer previousKey = keys.get(i-1);
    Integer previousValue = map.get(previousKey);

    Integer nextKey = keys.get(i+1);
    Integer nextValue = map.get(nextKey);

    if(previousValue.intValue() != value.intValue() || nextValue.intValue() != value.intValue()) {
        resultKeys.add(key);
    }
}

// last key will always be in the result map, add it
resultKeys.add(keys.get(keys.size()-1));

// make a map out of you list
Map<Integer, Integer> resultMap = resultKeys.stream()
        .collect(Collectors.toMap(k -> k, map::get));

Map<Integer, Integer> resultTreeMap = new TreeMap<>();
resultTreeMap.putAll(resultMap);

Лямбда-версия

// get the sorted list of keys
List<Integer> keys = new ArrayList<>(map.keySet());
Collections.sort(keys);

Map<Integer, Integer> resultMap = 
        IntStream.range(1, keys.size()-1)
        .boxed()
        .map(i -> setToNullIfNotKept(keys, i))
        .filter(Objects::nonNull)
        .collect(Collectors.toMap(k -> k, map::get));

// first key will always be in the result map, add it
resultMap.put(keys.get(0), map.get(keys.get(0)));
// last key will always be in the result map, add it
Integer lastKey = keys.get(keys.size() - 1);
resultMap.put(lastKey, map.get(lastKey));

Map<Integer, Integer> resultTreeMap = new TreeMap<>();
resultTreeMap.putAll(resultMap);

Утилита метод, чтобы обнулить неРазыскиваемые индексы:

private static Integer setToNullIfNotKept(List<Integer> keys, Integer i) {
    Integer key = keys.get(i);
    Integer value = map.get(key);

    Integer previousKey = keys.get(i-1);
    Integer previousValue = map.get(previousKey);

    Integer nextKey = keys.get(i+1);
    Integer nextValue = map.get(nextKey);

    if(previousValue.intValue() != value.intValue() || nextValue.intValue() != value.intValue()) {
        return key;
    }
    return null;
}

Выходные данные

Учитывая, что входная карта:

Map<Integer, Integer> map = new TreeMap<>();
map.put(1, 1);
map.put(2, 1);
map.put(3, 1);
map.put(4, 1);
map.put(5, 2);
map.put(6, 2);
map.put(7, 2);
map.put(8, 1);
map.put(9, 1);
map.put(10, 1);

Они оба выводят следующую карту:

{1=1, 4=1, 5=2, 7=2, 8=1, 10=1}
0 голосов
/ 27 сентября 2019

Реализация, использующая список вместо карты входных значений:

    final List<Double> input = getInputList();

    final Map<Integer, Double> result = new LinkedHashMap<>();
    if (input.isEmpty()) {
        return result;
    }

    boolean firstOccurrence = true;
    for (int i = 0; i < input.size() - 1; i++) {
        final Double current = input.get(i);
        final Double next = input.get(i + 1);
        if (firstOccurrence || !current.equals(next)) {
            result.put(i, current);
        }
        firstOccurrence = !current.equals(next);
    }
    result.put(input.size() - 1, input.get(input.size() - 1));
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...