递归地展开包含未知层级嵌套数组和映射的嵌套映射

4

我有一个嵌套的HashMap,其中包含以String为键的ListMapString值。我想要将它们展平,如下所示。

以下是数据:

import java.util.*;
import java.util.stream.*;
public class MyClass {
    public static void main(String args[]) {
        Map<String, Object> dates = new HashMap<String, Object>() {{
            put("1999", new HashMap<String, Object>() {{
                put("3", Arrays.asList("23", "24", "25"));
                put("4", Arrays.asList("1", "2", "3"));
            }});
            put("2001", new HashMap<String, Object>() {{
                put("11", new HashMap<String, Object>() {{
                    put("7", Arrays.asList("23", "24", "25"));
                    put("9", Arrays.asList("1", "2", "3"));
                }});
                put("12", "45");
            }});
        }};
        System.out.println(dates);
    }
}

地图看起来像:

{2001={11={7=[23, 24, 25], 9=[1, 2, 3]}, 12=45},
 1999={3=[23, 24, 25], 4=[1, 2, 3]}}

地图的展开应该是这样的:
{2001.11.7.1=23, 2001.11.7.2=24, 2001.11.7.3=25, 2001.11.9.1=1, 2001.11.9.2=2, 
 2001.11.9.3=3, 2001.12=45, 1999.3.1=23, 1999.3.2=24, 1999.3.3=25,
 1999.4.1=1, 1999.4.2=2, 1999.4.3=3}

注意:嵌套数组或映射的层数未知,可能超过2层。


如果关卡数量未知,您应考虑实现递归方法。为此,请考虑递归部分和递归结束条件。 - genius42
@genius42 是的,我需要一个检查程序,直到所有映射或数组都被展平。问题是我对Java不熟悉,无法理解如何实现这一点,这就是为什么我在这里发布问题的原因。我非常感谢附带解释的解决方案。 - Space Impact
我目前没有时间为您提供解决方案。我可以稍后或明天做到这一点。但是,为了给您一个想法:看看您有哪些不同的情况(Map或List),并处理它们。Map应该递归处理,而List则不应该。 - genius42
3个回答

2
您可以使用递归来展平Map。每次遇到Map时,通过展平该Map进行递归;当遇到List时,请迭代它并将索引添加到当前键中。否则,单个值可以轻松设置。在此处查看下面的代码演示。
public static Map<String, Object> flatten(final Map<String, Object> map) {
    return flatten("", map, new HashMap<>());
    //use new TreeMap<>() to order map based on key
}

@SuppressWarnings("unchecked")//recursive helper method
private static Map<String, Object> flatten(final String key, final Map<String, Object> map,
        final Map<String, Object> result) {
    final Set<Map.Entry<String, Object>> entries = map.entrySet();
    if (!entries.isEmpty()) {
        for (final Map.Entry<String, Object> entry : entries) {
            //iterate over entries
            final String currKey = key + (key.isEmpty() ? "" : '.') + entry.getKey();
           //append current key to previous key, adding a dot if the previous key was not an empty String
            final Object value = entry.getValue();
            if (value instanceof Map) {//current value is a Map
                flatten(currKey, (Map<String, Object>) value, result);//flatten Map
            } else if (value instanceof List) {//current value is a List
                final List<Object> list = (List<Object>) value;
                for (int i = 0, size = list.size(); i < size; i++) {
                    result.put(currKey + '.' + (i + 1), list.get(i));
                }
                //iterate over the List and append the index to the current key when setting value
            } else {
                result.put(currKey, value);//set normal value
            }
        }
    }
    return result;
}
public static void main(final String[] args){
    final Map<String, Object> flattened = flatten(dates);
    System.out.println(flattened);
}

1
那很完美,你能加一些解释或评论吗?谢谢。 - Space Impact
@SandeepKadapa 没问题。 - Unmitigated

1
你可以迭代这个映射,并根据其实例:MapListString,处理每个条目的值。由于嵌套数组或映射的级别未知,我已经修改了一下你的代码示例和扁平映射格式以使其更加清晰,也使用了TreeMap而不是HashMap来进行条目排序。
public static void main(String[] args) {
    TreeMap<String, Object> treeMap = new TreeMap<String, Object>() {{
        put("1999", new TreeMap<String, Object>() {{
            put("3", Arrays.asList("23", "24", "25"));
            put("4", Arrays.asList("1", "2", new TreeMap<String, Object>() {{
                put("10", "42");
            }}));
        }});
        put("2001", new TreeMap<String, Object>() {{
            put("11", new TreeMap<String, Object>() {{
                put("7", Arrays.asList("23", "24", "25"));
                put("9", Arrays.asList("1", "2", "3"));
            }});
            put("12", "45");
        }});
    }};

    TreeMap<String, String> flatMap = new TreeMap<>();
    processMap("", treeMap, flatMap);

    System.out.println(treeMap);
    System.out.println(flatMap);
}

private static void processMap(String prefix,
                               Map<String, Object> map,
                               Map<String, String> flatMap) {
    for (Map.Entry<String, Object> entry : map.entrySet()) {
        String key = entry.getKey();
        Object value = entry.getValue();
        processEntry(prefix, key, value, flatMap);
    }
}

private static void processList(String prefix,
                                List<Object> list,
                                Map<String, String> flatMap) {
    for (int i = 0; i < list.size(); i++) {
        String key = String.valueOf(i + 1);
        Object value = list.get(i);
        processEntry(prefix, key, value, flatMap);
    }
}

@SuppressWarnings("unchecked")
private static void processEntry(String prefix,
                                 String key,
                                 Object value,
                                 Map<String, String> flatMap) {
    if (value instanceof Map) {
        processMap(prefix + key + ".", (Map<String, Object>) value, flatMap);
    } else if (value instanceof List) {
        processList(prefix + key + ":", (List<Object>) value, flatMap);
    } else if (value instanceof String) {
        flatMap.put(prefix + key, (String) value);
    }
}

示例地图:

{1999={3=[23, 24, 25], 4=[1, 2, {10=42}]},
 2001={11={7=[23, 24, 25], 9=[1, 2, 3]}, 12=45}}

压平的地图:

{1999.3:1=23, 1999.3:2=24, 1999.3:3=25, 1999.4:1=1, 1999.4:2=2, 1999.4:3.10=42,
 2001.11.7:1=23, 2001.11.7:2=24, 2001.11.7:3=25,
 2001.11.9:1=1, 2001.11.9:2=2, 2001.11.9:3=3, 2001.12=45}

相反: 从平面图表示中恢复值树


0
  private static Map<String, String> flatten(Map<String, Object> m) {
    var pairs = new ArrayList<String[]>();
    flatten(null, m, pairs);
    return pairs.stream().collect(Collectors.toMap(x -> x[0], x -> x[1]));
  }
  private static void flatten(String prefix, Map<String, Object> m, List<String[]> pairs) {
    for (var e : m.entrySet()) {
      var k = e.getKey();
      var o = m.get(k);
      if (o instanceof String s) {
        pairs.add(new String[]{makeKey(prefix, k), s});
      } else if (o instanceof List l) {
        IntStream.range(0, l.size())
            .mapToObj(i -> new String[]{
                makeKey(prefix, k + "." + (i +1)), 
                l.get(i).toString()
            })
            .forEach(p -> pairs.add(p));
      } else if (o instanceof Map nestedMap) {
        flatten(makeKey(prefix, k), nestedMap, pairs);
      }
    }
  }
  private static String makeKey(String prefix, String key) {
     return  String.join(".", prefix == null ? List.of(key) : List.of(prefix,key));
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接