java - 如何使用两张 map 并制作另一张 map ?

标签 java dictionary hashmap guava

我有两个JSON,每个JSON都将其加载到Map中。为了简化示例,我简化了JSON。

第一个JSON

{
    "abc": "2",
    "plmtq": "hello+world",
    "lndp": "def",
    "yyt": "opi"
}


我将这个JSON作为key:value对加载到地图中。此处abc是键,而2是值。我们称之为mapA。这是一个字符串到字符串映射。

第二个JSON

{
    "count": {
        "linkedTo": "abc"
    },
    "title": {
        "linkedTo": "plmtq",
        "decode": "true"
    },
    "cross": {
        "linkedTo": "lndp"
    },
    "browse": {
        "linkedTo": "lndp"
    }
}


我也在地图中加载此JSON。我们称之为mapB。这是Map<String, Map<Tag, String>>。因此,在mapB中,键是count,值是另一个映射,其中键是linkedTo标签枚举,值是abc字符串。其他人也一样。如果它使此问题更简单,我也可以采用其他格式。

这里Tag是具有这些值的枚举类。目前,它只有两个值,但通常大约有7-8个值。

  public enum Tag {
    linkedTo, decode;
  };


例如:在这里linkedTo表示abc的值将进入count变量。类似地,plmtq的值进入title变量,但URL解码为decode字段,且该字段为true。

问题陈述:

现在,我需要使用这两个映射并制作一个新的映射,看起来像这样,它也将是String和String。

count=2
title=hello world // as you can see this got URL decoded with UTF-8 format.
cross=def
browse=def
yyt=opi


因此,Tag类中的每个枚举都有特殊含义,我需要相应地执行某些操作。现在它有两个,但通常有7-8个。解决此问题的最佳和有效方法是什么?

  private static Map<String, String> generateMap(final Map<String, String> mapA,
      final Map<String, Map<Tag, String>> mapB) {
    Map<String, String> newMap = new HashMap<>();
    for (Entry<String, Map<Tag, String>> entry : mapB.entrySet()) {
      String eventTag = entry.getKey();

      // I am confuse how to proceed further
    }
    return newMap;
  }


更新:-

与dimo讨论之后,我们为第二张地图提出了这个新的JSON设计:

第二个JSON

{
  "abc": {
      "renameTo": "count"
  },
  "plmtq": {
      "renameTo": "title",
      "decode": "true"
  },
  "lndp": {
      "renameTo": ["cross", "browse"],
     "decode": "true"
  }
}


它基本上只是还原格式。因此,在newMap项中的键将是count,其值将是abc的值。类似地,对于lndp情况,在newMap中,键将是crossbrowse,并且其值将是def,且URL已解码。

最佳答案

我将Java 7用于大多数此答案,但在底部有一些Java 8注释。

我认为有两个关键的见解首先是很重要的:


使用清晰的类,方法和变量名-名称应传达含义和目的,而mapAmapB之类的标识符则不能,因此很难推断出您要解决的问题。
将您的问题分成smaller parts;孤立地解决这些部分,然后将它们放在一起。


我将在此处尝试应用这两个概念。




将JSON转换为有意义且有用的数据结构(即不仅仅是一堆Map对象)。您的第二个JSON具有清晰的结构,很难将其用作嵌套地图。与其尝试创建复杂的算法来处理卷积数据,不如创建允许您有意义地表示数据并与之交互的对象。

考虑将第二个JSON表示为Map<String, Transformation>,其中Transformation类如下所示:

public class Transformation {
  /** Applies one or more transformations to the provided entry. */
  public Entry<String, String> apply(Entry<String, String> e) {
    // ...
  }
}


apply()是做什么的?我们将解决这个问题。到目前为止,拥有类结构已经足够了,因为现在我们可以编写一个简单的函数来执行所需的处理。
借助有用的数据结构,您手头的任务变得简单。考虑这样的事情(注意更清晰的函数和变量名):

private static Map<String, String> decodeMap(
    Map<String, String> encodedData,
    Map<String, Transformation> transformations) {
  Map<String, String> decodedData = new HashMap<>();
  for (Entry<String, String> e : encodedData.entrySet()) {
    Transformation t = transformations.get(e.getKey());
    if (t == null) {
      t = Transformation.NO_OP; // just passes the entry through
    }
    Entry<String, String> decoded = t.apply(e);
    decodedData.put(decoded.getKey(), decoded.getValue());
  }
  return decodedData;
}


这是一个非常简单的方法。我们遍历每个条目,应用(可能是无操作)转换,并为这些转换后的条目构建映射。假设我们的Transformation类完成了它的工作,那么显然该方法有效。

请注意,transformations的键是编码映射的键,而不是解码的键。使用它要容易得多,因此理想情况下,您需要将JSON转换为相同的结构,但是如果不能这样做,则只需要在构建Transformation对象之前将其翻转为代码即可。
现在,创建私有业务逻辑以在Transformation类中编码您的转换。这可能看起来像:

public class Transformation {
  public static final Transformation NO_OP =
      new Transformation(ImmutableMap.of());

  private final Map<String, String> tasks;

  public Transformation(Map<String, String> tasks) {
    // could add some sanity checks here that tasks.keySet() doesn't
    // contain any unexpected values
    this.tasks = tasks;
  }

  public Entry<String, String> apply(Entry<String, String> e) {
    String key = e.getKey();
    String value = e.getValue();
    for (Entry<String, String> task : tasks.entrySet()) {
      switch (task.getKey()) {
        case "linkedTo":
          // this assumes we've inverted the old/new mapping. If not pass
          // the new key name into the constructor and use that here instead
          key = task.getValue();
          break;
        case "decode":
          if (Boolean.valueOf(task.getValue())) {
            // or whichever decoding function you prefer
            value = URLDecoder.decode(value);
          }
          break;
        // and so on
        default:
          throw new IllegalArgumentException(
              "Unknown task " + task.getKey());
      }
    }
    return Maps.immutableEntry(key, value);
  }


Transformation类可以处理任意数量的任务,并且不需要其他代码来了解具体细节。
最后一步是将JSON解析为此Map<String, Transformation>结构。我将由您决定,因为这取决于您选择的JSON解析器,但是我建议使用Gson




您要求将lndp条目转换为具有相同值的两个单独的条目是一个奇怪的问题。在我看来,它很像x-y problem的味道-您正在寻求帮助以使这种复杂的转换逻辑起作用,而不是探索基础前提本身是否有缺陷。考虑在一个单独的问题中进行探讨,在该问题中您可以清楚说明开始的内容,尝试去的地方以及为什么当前的解决方案看起来最好。然后人们可以对策略本身提出建议。

也就是说,我们仍然可以将我的第二个要点应用于此问题。与其尝试定义支持多条目关系的转换逻辑,不如将一个后处理步骤添加到转换中。定义一个单独的数据结构,该结构编码应复制(在新地图中)哪些条目,例如

{
  "cross": ["browse"],
  ...
}


然后,decodeMap()看起来像这样:

private static Map<String, String> decodeMap(
    Map<String, String> encodedData,
    Map<String, Transformation> transformations,
    Map<String, List<String>> duplications) {
  // ... existing method body
  for (Entry<String, List<String>> e : duplications) {
    String value = checkNotNull(decodedData.get(e.getKey()));
    for (String newKey : e.getValue()) {
      decodedData.put(newKey, value);
    }
  }
  return decodedData;
}


这是将问题分成更小的部分,而不是一口气解决所有问题的另一个示例。您甚至可以将这些任务拆分为单独的方法,以使decodeMap()本身不会变得太复杂。



您可能会注意到Transformation类看起来很像Function-这是因为它本质上是一个。您可以添加implements Function<Entry<String, String>, Entry<String, String>>(或在Java 8 implements UnaryOperator<Entry<String, String>>中),并且可以像使用其他Transformation一样使用Function

特别是,这将允许您创建Transformation接口并一起组成简单的Transformation对象,而不是定义一个负责每个任务的大型类型。对于每个所需的任务,您都有一个专用的Transformation子类,例如:

public RenameKeyTransformation implements Transformation {
  private final renameTo;
  public RenameKeyTransformation(String renameTo) {
    this.renameTo = checkNotNull(renameTo);
  }

  public Entry<String, String> apply(Entry<String, String> e) {
    return Maps.immutableEntry(renameTo, e.getValue());
  }
}


等等。然后,您可以将它们与Functions.compose()(或Java 8的Function.andThen())组成,以创建应用所有所需转换的复合函数。例如:

Function<Entry<String, String>, Entry<String, String>> compound =
    Functions.compose(
        new RenameKeyTransformation("plmtq"),
        new UrlDecodeValue());


返回一个新函数,该函数会将重命名键操作和url-decode-value操作应用于给定条目。您可以重复地链接函数以根据需要应用尽可能多的转换。

第一个Transformation对象的概念化可能更简单(这是一个非常合理的解决方案),但是组成小功能的第二个想法是将上述封装概念带到另一个层次,将每个单独的转换任务彼此隔离并使之完成随时随地轻松添加更多转换。这是strategy pattern的示例,这是一种将要处理的数据与要执行的确切处理步骤分离的一种优雅方法。

关于java - 如何使用两张 map 并制作另一张 map ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40814420/

相关文章:

python - 电子邮件和 map 减少作业

java - hashmap和多个txt文件java

java - 如何迭代Nested Map和Multiset? - java / Guava

java - 如何使用 Play Framework 上传多个文件?

python - 我删除了我的字典,但我的 dict_keys 不介意,这是为什么?

java - 如何知道 Eclipse 工作区中的 java 文件数量?

python - 多索引 Pandas 数据框到字典

java - 如何使用 Java Streams 为 HashMap 的多个键插入相同的值

Java List forEach Lambda 表达式 - 第一个元素并遍历所有元素

java - 使多个 JPanel 及其 JTable 适合 JFrame