Версия для печати
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум на Исходниках.RU > Java > Поиск value в Map<String, Object> и последующая замена


Автор: DRON666 02.10.19, 11:22
Всем привет, кто еще здесь остался :lol: ! Задача в следующем. Есть сложная мапа: Map<String, Object> где Object может быть String, Long, List<Object>, Map<String, Object>. Так вот, в дебрях этой мапы мне надо найти все значения, которые являются String и которые удовлетворяют некому условию, например, регулярному выражению, и произвести замену в строке (заменить часть строки), обновив исходный Map. Уровень вложенности объектов не большой - не более 4 уровней. Как заменить value в Map или List я знаю. Вопрос в том, как найти. Ничего умнее рекурсивного поиска в лоб я не придумал с базовой конструкцией вида:
<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    void findInMap(Map<String, Object> map){
            for(Map.Entry<String, Object> entry: map.entrySet()){
                Object objValue = entry.getValue();
                if(objValue instanceof String){
                   //определяем, соответствует ли строка условию,
                   //если соответствует, производим замену
                }else if(objValue instanceof Long){
                   //пропускаем
                }else if(objValue instanceof List){
                   //ищем в List'е, отдельный метод, но по сути тоже самое что и findInMap
                }else if(objValue instanceof Map){
                   //ищем в Map
                   findInMap( (Map<String, Object>)objValue );
                }else{
                   //не понятный объект, кидаем исключение
                }
            }
    }


Код получается не сказать что сложный, но не маленький. Может есть более красивое решение? Думал насчет стримов (Stream API), но так и не придумал как их использовать для данной задачи, правда я и не очень хорошо эти стримы понимаю.

Автор: korvin 02.10.19, 19:33
Цитата DRON666 @
Думал насчет стримов (Stream API)

Они тут ничем не помогут.

Цитата DRON666 @
Код получается не сказать что сложный, но не маленький.

Да не, вполне небольшой.

Зачем кидать исключение на непонятный объект? Может его просто пропускать мимо вместе с Long'ами?

Добавлено
Ну например:

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.TreeMap;
    import java.util.function.UnaryOperator;
    import java.util.regex.Pattern;
     
    import static java.util.stream.Collectors.toList;
    import static java.util.stream.Collectors.toMap;
     
    final class FindInMap {
     
        public static void main(String[] args) {
            final Map<String, Object> map = Map.of(
                    "foo", "bar",
                    "Foo", "Bar",
                    "FOO", "BAZ",
                    "gee", List.of(123, "bar", "BAZ"),
                    "qux", Map.of("foo", "Bar", "gee", 567)
            );
            printSortedMap(map);
            final var pattern = Pattern.compile("([bB])ar");
            final var aaargh = new Updater<>(String.class, s -> pattern.matcher(s).matches()
                    ? s.replace("ar", "AAARGH!!!")
                    : s);
            final var aaarghedMap = aaargh.update(map);
            printSortedMap(aaarghedMap);
        }
     
        private static <K, V> void printSortedMap(Map<K, V> map) {
            System.out.println(new TreeMap<>(map));
        }
     
        static final class Updater<T> {
     
            private final Class<T> cls;
            private final UnaryOperator<T> mapper;
     
            Updater(Class<T> cls, UnaryOperator<T> mapper) {
                this.cls = cls;
                this.mapper = mapper;
            }
     
            Map<String, Object> update(Map<String, Object> map) {
                return map.entrySet().stream()
                        .map(this::update)
                        .collect(toMap(Entry::getKey, Entry::getValue));
            }
     
            List<Object> update(List<Object> list) {
                return list.stream()
                        .map(this::update)
                        .collect(toList());
            }
     
            Entry<String, Object> update(Entry<String, Object> entry) {
                return new Pair<>(entry.getKey(), update(entry.getValue()));
            }
     
            Object update(Object o) {
                if (cls.isAssignableFrom(o.getClass())) {
                    @SuppressWarnings("unchecked")
                    final var value = (T) o;
                    return mapper.apply(value);
                } else if (o instanceof List) {
                    @SuppressWarnings("unchecked")
                    final var list = (List<Object>) o;
                    return update(list);
                } else if (o instanceof Map) {
                    @SuppressWarnings("unchecked")
                    final var map = (Map<String, Object>) o;
                    return update(map);
                } else {
                    return o;
                }
            }
        }
     
        private static final class Pair<A, B> implements Entry<A, B> {
     
            private final A first;
            private final B second;
     
            private Pair(A first, B second) {
                this.first = first;
                this.second = second;
            }
     
            @Override
            public A getKey() {
                return first;
            }
     
            @Override
            public B getValue() {
                return second;
            }
     
            @Override
            public B setValue(B value) {
                throw new UnsupportedOperationException("Immutable object");
            }
        }
    }


=>

<{CODE_COLLAPSE_OFF}><{CODE_WRAP_OFF}>
    {FOO=BAZ, Foo=Bar, foo=bar, gee=[123, bar, BAZ], qux={gee=567, foo=Bar}}
    {FOO=BAZ, Foo=BAAARGH!!!, foo=bAAARGH!!!, gee=[123, bAAARGH!!!, BAZ], qux={gee=567, foo=BAAARGH!!!}}

Автор: DRON666 04.10.19, 07:31
Ого, очень интересно. О таком функциональном решении я не думал, углубиться в стримы пришлось, спасибо.

Powered by Invision Power Board (https://www.invisionboard.com)
© Invision Power Services (https://www.invisionpower.com)