Given an array with some key-value pairs:
[
{'a': 1, 'b': 1},
{'a': 2, 'b': 1},
{'a': 2, 'b': 2},
{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 2, 'b': 1, 'c': 1},
{'a': 2, 'b': 1, 'c': 2}
]
I want to find an intersection of these pairs. Intersection means to leave only those elements, that can be covered by others, or unique. For example,
{'a': 1, 'b': 1, 'c': 1}
and {'a': 1, 'b': 1, 'c': 2}
fully cover {'a': 1, 'b': 1}
, while {'a': 2, 'b': 2}
is unique. So, in
[
{'a': 1, 'b': 1},
{'a': 2, 'b': 1},
{'a': 2, 'b': 2},
{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 2, 'b': 1, 'c': 1},
{'a': 2, 'b': 1, 'c': 2}
]
after finding the intersection should remain
[
{'a': 2, 'b': 2},
{'a': 1, 'b': 1, 'c': 1},
{'a': 1, 'b': 1, 'c': 2},
{'a': 2, 'b': 1, 'c': 1},
{'a': 2, 'b': 1, 'c': 2}
]
I tried to iterate over all pairs and find covering pairs comparing with each other, but time complexity equals to O(n^2)
. Is it possible to find all covering or unique pairs in linear time?
Here is my code example (O(n^2)
):
public Set<Map<String, Integer>> find(Set<Map<String, Integer>> allPairs) {
var results = new HashSet<Map<String, Integer>>();
for (Map<String, Integer> stringToValue: allPairs) {
results.add(stringToValue);
var mapsToAdd = new HashSet<Map<String, Integer>>();
var mapsToDelete = new HashSet<Map<String, Integer>>();
for (Map<String, Integer> result : results) {
var comparison = new MapComparison(stringToValue, result);
if (comparison.isIntersected()) {
mapsToAdd.add(comparison.max());
mapsToDelete.add(comparison.min());
}
}
results.removeAll(mapsToDelete);
results.addAll(mapsToAdd);
}
return results;
}
where MapComparison is:
public class MapComparison {
private final Map<String, Integer> left;
private final Map<String, Integer> right;
private final ComparisonDecision decision;
public MapComparison(Map<String, Integer> left, Map<String, Integer> right) {
this.left = left;
this.right = right;
this.decision = makeDecision();
}
private ComparisonDecision makeDecision() {
var inLeftOnly = new HashSet<>(left.entrySet());
var inRightOnly = new HashSet<>(right.entrySet());
inLeftOnly.removeAll(right.entrySet());
inRightOnly.removeAll(left.entrySet());
if (inLeftOnly.isEmpty() && inRightOnly.isEmpty()) {
return EQUALS;
} else if (inLeftOnly.isEmpty()) {
return RIGHT_GREATER;
} else if (inRightOnly.isEmpty()) {
return LEFT_GREATER;
} else {
return NOT_COMPARABLE;
}
}
public boolean isIntersected() {
return Set.of(LEFT_GREATER, RIGHT_GREATER).contains(decision);
}
public boolean isEquals() {
return Objects.equals(EQUALS, decision);
}
public Map<String, Integer> max() {
if (!isIntersected()) {
throw new IllegalStateException();
}
return LEFT_GREATER.equals(decision) ? left : right;
}
public Map<String, Integer> min() {
if (!isIntersected()) {
throw new IllegalStateException();
}
return LEFT_GREATER.equals(decision) ? right : left;
}
public enum ComparisonDecision {
EQUALS,
LEFT_GREATER,
RIGHT_GREATER,
NOT_COMPARABLE,
;
}
}