Обходите вложенные Map рекурсивно, накапливая путь ключа. Для надёжного простого контракта запретите разделитель внутри ключей и циклические ссылки.
Рекурсивное развертывание вложенной Map в однородный словарь
Обходите вложенные Map рекурсивно, накапливая путь ключа. Для надёжного простого контракта запретите разделитель внутри ключей и циклические ссылки.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Создайте функцию, которая принимает вложенную структуру типа Map<String, Object> и возвращает плоскую Map, где:
- Внутренние Map раскрываются рекурсивно;
- Ключи образуются путем соединения родительского и дочернего ключей с помощью заданного разделителя;
- Значения, не являющиеся Map, копируются без изменений.
Ответ
Обходите вложенные Map рекурсивно, накапливая путь ключа. Для надёжного простого контракта запретите разделитель внутри ключей и циклические ссылки.
static Map<String, Object> flatten(Map<String, Object> input, String separator) {
if (separator == null || separator.isEmpty()) throw new IllegalArgumentException();
Map<String, Object> result = new LinkedHashMap<>();
flattenInto(input, null, separator, result);
return result;
}
static void flattenInto(Map<?, ?> input, String prefix, String sep,
Map<String, Object> result) {
for (var entry : input.entrySet()) {
if (!(entry.getKey() instanceof String)) throw new IllegalArgumentException();
String key = (String) entry.getKey();
if (key.isEmpty() || key.contains(sep)) throw new IllegalArgumentException();
String path = prefix == null ? key : prefix + sep + key;
if (entry.getValue() instanceof Map<?, ?>)
flattenInto((Map<?, ?>) entry.getValue(), path, sep, result);
else {
if (result.containsKey(path)) throw new IllegalArgumentException("Collision");
result.put(path, entry.getValue());
}
}
}
Импорты из java.util опущены. Пустые вложенные карты не дают листовых записей; null-значения сохраняются. Для произвольных ключей вместо запрета нужен формат экранирования; для циклов — отдельная защита.