1

I have an arraylist of objects called Variable (ArrayList-Variable-) (The - is instead of the <, which otherwise will trigger the quote). This Variable object has two fields: name and domain, where the name is a String and the domain is an array of int. I want to merge the variables that have the same name, specifically I want to have a single variable with that name and with domain equal to the union of all the domains of the variables with the same name (without duplicates).

For example:

ArrayList : [VarA: 1, VarA: 6, VarB: 9, VarB: 3, VarA: 4, VarC: 2, VarC: 1]

What I would like to have is: [VarA: {1,6,4}, VarB: {9,3}, VarC: {2,1}]

How can I achieve that?

1
  • You said each object had an array of ints yet you only show one value per Var in your example. That impacts any help that we might provide. Please clarify. Commented Jun 11, 2021 at 17:29

5 Answers 5

1

You can simply use HashMap to store such data Let say variable for your ArrayList is arr then :-

HashMap<String, Set<Integer>> map = new HashMap<>();
for(Variable var:arr) {
   Set<Integer> set = map.getOrDefault(var.name, new HashSet<Integer>());
   set.add(var.domain);
   map.put(var.name, set);
}

Then map (HashMap) will contain desired results which you can convert if required like again to ArrayList.

Sign up to request clarification or add additional context in comments.

Comments

0

I would use a Hashmap of list to achieve that.

for (Tuple item: list) {
    if (!map.containsKey(item.key)) {
        map.put(item.key, new LinkedList<String>());
    }
    map.get(item.key).add(item.value);
}

Then using map.values() , you can recover a collection of the value Lists.

Comments

0

With Java 8 Stream you can do it as below:

Map<String, Set<Integer>> result = v.stream().collect(
        Collectors.groupingBy(Variable::getDomain, 
            HashMap::new, Collectors.mapping(Variable::getVal, Collectors.toSet()))
      );

Test Code:

List<Variable> v = new ArrayList<Variable>();
v.add(new Variable("A", 1));
v.add(new Variable("A", 2));
v.add(new Variable("B", 4));
v.add(new Variable("C", 7));
v.add(new Variable("C", 7));

 Map<String, Set<Object>> result = v.stream().collect(
    Collectors.groupingBy(Variable::getDomain, 
        HashMap::new, Collectors.mapping(Variable::getVal, Collectors.toSet()))
  );
System.out.println(result);

Output: {A=[1, 2], B=[4], C=[7]}

Variable Class:

class Variable {

  private String domain;

  private int val;

  public Variable(String domain, int val) {
    this.domain = domain;
    this.val = val;
  }

  /**
   * @return the domain
   */
  public String getDomain() {
    return domain;
  }

  /**
   * @param domain
   *          the domain to set
   */
  public void setDomain(String domain) {
    this.domain = domain;
  }

  /**
   * @return the val
   */
  public int getVal() {
    return val;
  }

  /**
   * @param val
   *          the val to set
   */
  public void setVal(int val) {
    this.val = val;
  }

}

2 Comments

The OP state in their question. the domain is an array of int.
@WJS In example it mentions the ArrayList. And this is sample code, because conversion to-from array/list is quite simple.
0

You already have a list? You can use groupingBy.

Map<String, List<Integer>> grouped = list.stream().collect( Collectors.groupingBy( Variable::getGroup ) );

Comments

0
  • this creates a List of some instances (records) that have duplicate values in the array for similar names.
  • It uses a hashMap to store the name and domain by taking advantage of computeIfAbsent to first create the set if it doesn't exist, otherwise, add the arrays to it. The purpose of the set is to eliminate duplicates.
// your list of objects each with a name and Integer[] domain   
List<Variable> vars = List.of(new Variable("var1", 1, 2, 3),
        new Variable("var2", 4, 5, 6), new Variable("var1", 1, 9, 10),
        new Variable("var5", 1, 6, 8), new Variable("var2", 4, 8, 9));
    
Map<String, Set<Integer>> result = new HashMap<>();
for (Variable v : vars) {
    result.computeIfAbsent(v.name, k -> new HashSet<>())
            .addAll(Arrays.asList(v.domain));
}
    
result.entrySet().forEach(System.out::println);

prints

var5=[1, 6, 8]
var2=[4, 5, 6, 8, 9]
var1=[1, 2, 3, 9, 10]

The Variable class per your description.

class Variable {
    private String name;
    private Integer[] domain;
    public Variable(String name, Integer... domain) {
        super();
        this.name = name;
        this.domain = domain;
    }
    public String getName() {
        return name;
    }
    public Integer[] getDomain() {
        return domain;
    }   
}

You can also do it using streams. I chose to use the flatMapping collector to combine the domain arrays into a set for each name.

Map<String,Set<Integer>> result = vars.stream().collect(
        Collectors.groupingBy(v->v.getName(),
           Collectors.flatMapping(
                   v->Arrays.stream(v.getDomain()),
                           Collectors.toSet())));

This yields the same as above. Both of the above examples assume an Integer array to hold the domain items. A List<Integer> would be even better. Primitive arrays aren't readily supported by Arrays.asList or stream operations but there are workarounds.

Comments

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.