如何使用两张地图并制作另一张地图?

时间:2016-11-26 02:40:44

标签: java dictionary hashmap guava

我有两个JSON和每个JSON我在Map中加载它。为了简化示例,我缩短了JSON。

第一个JSON

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

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

第二个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变量,但解码为decode字段的URL仍然存在且为真。

问题陈述:

现在我需要使用这两个地图并制作一个看起来像这样的新地图,它也将是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设计:

SECOND JSON

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

基本上只是恢复格式。因此,newMap密钥将为count,其值为abc。类似地,对于lndp情况,newMap密钥将为crossbrowse,并且其值为def且网址已解码。

2 个答案:

答案 0 :(得分:3)

我正在使用Java 7作为答案的大部分内容,但底部有一些Java 8评论。

我认为有两个重要的见解是重要的:

  • 使用明确的类,方法和变量名称 - 名称应该表达意义和目的,而mapAmapB之类的标识符则不会,因此很难推断出您正在尝试的问题解决。
  • 将您的问题分成smaller parts;单独解决这些部分,然后将它们放在一起。

我会尝试在这里应用这两个概念。

  1. 将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()做什么?我们会做到这一点。现在,拥有类结构已经足够了,因为现在我们可以编写一个简单的函数来进行所需的处理。

  2. 通过有用的数据结构,您手头的任务变得简单。考虑这样的事情(注意更清晰的函数和变量名称):

    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对象之前在代码中翻转它们。 / p>

  3. 现在创建私有业务逻辑,对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类可以处理任意数量的任务,而其他任何代码都不需要知道具体细节。

  4. 最后一步是将JSON解析为此Map<String, Transformation>结构。我会把它留给你,因为它取决于你选择的JSON解析器,但我建议使用Gson

  5. 您要求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的一个示例,这是将正在处理的数据与要执行的确切处理步骤分离的优雅方法。

答案 1 :(得分:1)

这个怎么样

String query = "MATCH (i:Item) "
                + "WHERE id(i)= {1} "
                + "SET i.active = NOT i.active";

或者用于解码的任何内容......