以递归方式展平包含未知级别的嵌套数组和映射的嵌套映射

Posted

技术标签:

【中文标题】以递归方式展平包含未知级别的嵌套数组和映射的嵌套映射【英文标题】:Flatten nested Map containing with unknown level of nested Arrays and Maps recursively 【发布时间】:2020-10-25 03:44:05 【问题描述】:

我有一个嵌套的HashMapString 键,其中包含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 是的,我需要一些东西来检查,直到没有地图或数组并且一切都变平为止。问题是我是java新手,不明白如何实现这个,这就是我在这里发布问题的原因。我非常感谢有解释的解决方案。 我目前没有时间为您提供解决方案。我可以稍后或明天做。但是给您一个想法:查看您拥有的不同案例(地图或列表)并处理它们。地图应该递归处理,而列表不应该。 【参考方案1】:

您可以使用递归来展平Map。每次遇到Map,通过展平Map 进行递归;当您遇到List 时,对其进行迭代并将索引添加到当前键。否则可以简单地设置单个值。请参阅下面的代码here。

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);

【讨论】:

那太完美了,您可以添加一些解释或 cmets。谢谢。 @SandeepKadapa 没问题。【参考方案2】:

您可以遍历此映射,并根据以下实例处理每个条目值:MapListString。由于嵌套数组或映射的级别未知,为了清楚起见,我对您的代码示例和平面映射格式进行了一些修改,并且我使用TreeMap 而不是HashMap 进行条目排序。 p>

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

对面:Restoring a value tree from its flat map representation.

【讨论】:

以上是关于以递归方式展平包含未知级别的嵌套数组和映射的嵌套映射的主要内容,如果未能解决你的问题,请参考以下文章

以独特的方式展平包含嵌套字典的列表的数据框列

使用递归(并且不使用循环)展平嵌套数组

展平嵌套对象以将其属性映射到目标对象

以更快的方式展平和扩展 json

javascript 通过递归来展平嵌套数组Ex:[1,2,3,[4,5,[7,8,[10,11,[12,13,[[[[[[14]]]]]]]]]] ]]

是否有展平嵌套元素列表的功能?