0
\$\begingroup\$

I currently have 2 Maps intersectionDiffLeft, intersectionDiffRight with the structure of Map<Boolean, List<Map<String, Object>>

Where if the boolean is true it means it is considered a matching record. If it is true there will be 1 true record from left side and one true record on right side. There can be zero to many in this map. Same goes for if the boolean is false, meaning those records don't match another one on the opposite side, however the same amount on each doesn't need to be the same like if the boolean is true.

I'm looking for a way to create a list of objects such as this one to return after I have finished.

@Value
public class ReconciliationResult {

    Map<String, Object> recordValue;
    List<Object> keyValues;
    Origin origin;
    ReconciliationResultStatus status;

    public enum ReconciliationResultStatus {
        INVALID_KEY("INVALID_KEY"),
        DUPLICATE_KEY("DUPLICATE_KEY"),
        MATCHING("MATCHING"),
        NON_MATCHING("NON_MATCHING");
        private final String text;

        ReconciliationResultStatus(String text) {
            this.text = text;
        }
    }

    public enum Origin {
        LEFT_SIDE("lhs"),
        RIGHT_SIDE("rhs");

        private final String side;

        Origin(String side) {
            this.side = side;
        }
    }

I currently have it implemented in a way where I am duplicating code and it works, but am looking for a more elegant solution instead of all of this duplication, note the creation of intersectionDiffRight is the same as intersectionDiffLeft except we use rhsMap, instead of lhsMap.

Please let me know if you need any other code from me.

    Map<Boolean, List<Map<String, Object>>> intersectionDiffRight =
            rhsMap
                    .entrySet()
                    .stream()
                    .collect(
                            Collectors.partitioningBy(
                                    entry -> lhsMap.containsKey(entry.getKey()),
                                    Collectors.mapping(Map.Entry::getValue, Collectors.toList())));

    List<Map<String, Object>> intersection = new ArrayList<>();
    intersection.addAll(intersectionDiffLeft.get(true));
    intersection.addAll(intersectionDiffRight.get(true));

    List<Map<String, Object>> difference = new ArrayList<>();
    difference.addAll(intersectionDiffLeft.get(false));
    difference.addAll(intersectionDiffRight.get(false));

    List<ReconciliationResult> reconResults = new ArrayList<>();
    for (Map<String, Object> map : intersectionDiffLeft.get(true)) {
      reconResults.add(new ReconciliationResult(map, Collections.singletonList(map.keySet()), ReconciliationResult.Origin.LEFT_SIDE, ReconciliationResult.ReconciliationResultStatus.MATCHING));
    }
        for (Map<String, Object> map : intersectionDiffLeft.get(false)) {
      reconResults.add(new ReconciliationResult(map, Collections.singletonList(map.keySet()), ReconciliationResult.Origin.LEFT_SIDE, ReconciliationResult.ReconciliationResultStatus.NON_MATCHING);
    }
        for (Map<String, Object> map : intersectionDiffRight.get(true)) {
      reconResults.add(new ReconciliationResult(map, Collections.singletonList(map.keySet()), ReconciliationResult.Origin.RIGHT_SIDE, ReconciliationResult.ReconciliationResultStatus.MATCHING));
    }
        for (Map<String, Object> map : intersectionDiffRight.get(false)) {
     reconResults.add(new ReconciliationResult(map, Collections.singletonList(map.keySet()), ReconciliationResult.Origin.RIGHT_SIDE, ReconciliationResult.ReconciliationResultStatus.NON_MATCHING));
    }
    
```
\$\endgroup\$
7
  • 4
    \$\begingroup\$ This data structure is pretty awkward, and I don't at face value buy that it best represents whatever you're actually doing. You should add more context about your realistic scenario. Particularly, it doesn't make much sense to map booleans as a key, and that should probably be split to two variables. \$\endgroup\$
    – Reinderien
    Commented Aug 30, 2022 at 21:38
  • \$\begingroup\$ Essentially it is a map that contains either the intersection or difference between the results of two queries. The queries are customizable so as long as each dataset we are comparing contain the same keys, we want to find what records on the left side match those on the right and vice versa. The problem we ran into while using sets, is that we only may want to compare certain keys to determine a match. For instance the ID field on LHS and RHS should be different, but we don't want to compare. The boolean is for keeping track of the record on each side if true=match, false=break. @Reinderien \$\endgroup\$
    – Beez
    Commented Aug 30, 2022 at 22:08
  • \$\begingroup\$ What is @Value? \$\endgroup\$
    – Reinderien
    Commented Aug 30, 2022 at 23:01
  • 2
    \$\begingroup\$ @Reinderien I suspect that @Value annotation is from Lombok, in which case it should cause a constructor to be generated, which would make the class compilable \$\endgroup\$
    – Sara J
    Commented Aug 30, 2022 at 23:39
  • 2
    \$\begingroup\$ I set your code to the original version because your question has received an answer involving the original code. For details you can check someone-answers. \$\endgroup\$ Commented Aug 31, 2022 at 5:49

1 Answer 1

3
\$\begingroup\$

Your INVALID_KEY and DUPLICATE_KEY are never used, so delete them and reduce that enum to a boolean.

Keep your Origin enum, but delete the explicit constructor and the string member.

Partitioning is not useful here. You can just find the intersection of the two key sets, and then check for membership in a utility function.

If you want an implicit constructor for ReconciliationResult, I suggest just using a record instead.

keyValues is not useful as a member; delete it. If you really need that later, add a convenience method to derive it from your recordValue.

Suggested

import java.util.*;
import java.util.stream.Stream;

public class Main {
    private record ReconciliationResult(
        Map<String, Object> recordValue,
        Origin origin,
        boolean matched
    ){
        public enum Origin {
            LEFT, RIGHT;
        }

        static Stream<ReconciliationResult> forSide(
            Map<String, Map<String, Object>> side,
            Set<String> shared,
            Origin origin
        ) {
            return side.entrySet().stream()
                .map(entry -> new ReconciliationResult(
                    entry.getValue(), origin, shared.contains(entry.getKey())
                ));
        }
    }

    private static Stream<ReconciliationResult> findIntersection(
        Map<String, Map<String, Object>> lhsMap,
        Map<String, Map<String, Object>> rhsMap
    ) {
        var sharedKeys = new HashSet<String>(lhsMap.keySet());
        sharedKeys.retainAll(rhsMap.keySet());

        return Stream.concat(
            ReconciliationResult.forSide(lhsMap, sharedKeys, ReconciliationResult.Origin.LEFT),
            ReconciliationResult.forSide(rhsMap, sharedKeys, ReconciliationResult.Origin.RIGHT)
        );
    }

    public static void main(String[] args) {
        var result = findIntersection(
            Map.of(
                "left1", Map.of("1", 2, "3", 4),
                "shared", Map.of("a", "b")
            ),
            Map.of(
                "right1", Map.of("5", 6, "7", 8),
                "shared", Map.of("c", "d")
            )
        ).toList();
    }
}
\$\endgroup\$
3
  • \$\begingroup\$ I think this is close, I have yet to check it, but I would like to compare the entry set, and the whole reason I went with the data structure I did, is because if the value for each key that I need, i.e. (entity, instrument, etc...) don't match the other side it shouldn't be qualified as a match. That is why in this case, if I'm understanding it correctly if the ID fields are different they wouldn't be an intersection (match). We need not only the keys, but the values as well, with the ability to exclude certain keys. I will post my full code in an edit. @Reinderien \$\endgroup\$
    – Beez
    Commented Aug 31, 2022 at 5:15
  • \$\begingroup\$ So far as I'm aware this accomplishes the same thing. I encourage you to run it with a breakpoint at the end and examine the results. If there is an error please let me know what the output should actually be. \$\endgroup\$
    – Reinderien
    Commented Aug 31, 2022 at 11:43
  • 1
    \$\begingroup\$ I see what happened - my guess about your key structure was wrong. I'll give it another shot tonight. \$\endgroup\$
    – Reinderien
    Commented Aug 31, 2022 at 12:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.