public static Map<PCollection<?>, ReplacementOutput> tagged(
      Map<TupleTag<?>, PCollection<?>> original, POutput replacement) {
    Map<TupleTag<?>, TaggedPValue> originalTags = new HashMap<>();
    for (Map.Entry<TupleTag<?>, PCollection<?>> originalValue : original.entrySet()) {
      originalTags.put(
          originalValue.getKey(),
          TaggedPValue.of(originalValue.getKey(), originalValue.getValue()));
    }
    ImmutableMap.Builder<PCollection<?>, ReplacementOutput> resultBuilder = ImmutableMap.builder();
    Map<TupleTag<?>, PCollection<?>> remainingTaggedOriginals = new HashMap<>(original);
    Map<TupleTag<?>, PCollection<?>> taggedReplacements = PValues.expandOutput(replacement);
    for (Map.Entry<TupleTag<?>, PCollection<?>> replacementValue : taggedReplacements.entrySet()) {
      TaggedPValue mapped = originalTags.get(replacementValue.getKey());
      checkArgument(
          mapped != null,
          "Missing original output for Tag %s and Value %s Between original %s and replacement %s",
          replacementValue.getKey(),
          replacementValue.getValue(),
          original,
          replacement.expand());
      resultBuilder.put(
          replacementValue.getValue(),
          ReplacementOutput.of(
              mapped,
              TaggedPValue.of(
                  replacementValue.getKey(), (PCollection<?>) replacementValue.getValue())));
      remainingTaggedOriginals.remove(replacementValue.getKey());
    }
    checkArgument(
        remainingTaggedOriginals.isEmpty(),
        "Missing replacement for tagged values %s. Replacement was: %s",
        remainingTaggedOriginals,
        taggedReplacements);
    return resultBuilder.build();
  }
@Test
  public void taggedSucceeds() {
    PCollectionTuple original =
        PCollectionTuple.of(intsTag, ints).and(strsTag, strs).and(moreIntsTag, moreInts);

    Map<PCollection<?>, ReplacementOutput> replacements =
        ReplacementOutputs.tagged(
            PValues.expandOutput((POutput) original),
            PCollectionTuple.of(strsTag, replacementStrs)
                .and(moreIntsTag, moreReplacementInts)
                .and(intsTag, replacementInts));
    assertThat(
        replacements.keySet(),
        Matchers.containsInAnyOrder(replacementStrs, replacementInts, moreReplacementInts));
    ReplacementOutput intsReplacement = replacements.get(replacementInts);
    ReplacementOutput strsReplacement = replacements.get(replacementStrs);
    ReplacementOutput moreIntsReplacement = replacements.get(moreReplacementInts);

    assertThat(
        intsReplacement,
        equalTo(
            ReplacementOutput.of(
                TaggedPValue.of(intsTag, ints), TaggedPValue.of(intsTag, replacementInts))));
    assertThat(
        strsReplacement,
        equalTo(
            ReplacementOutput.of(
                TaggedPValue.of(strsTag, strs), TaggedPValue.of(strsTag, replacementStrs))));
    assertThat(
        moreIntsReplacement,
        equalTo(
            ReplacementOutput.of(
                TaggedPValue.of(moreIntsTag, moreInts),
                TaggedPValue.of(moreIntsTag, moreReplacementInts))));
  }