Concatenate set of objects, group by value in java

Viewed 569

I'm making a Set of objects that consists of name and value. My requirement is to create a new Set by grouping the Set based on objects that have the same value and concate the property name into a single string separated by ",".

Example:

   Name: Test,     value: Random value
   Name: Test 2,   value: Some random value
   Name: Test 3,   value: Random value

I want the result to be a Set like this:

Name: Test, Test 3,  value: Random value
Name: Test 2 ,   value: Some random value

The class for understanding:

public class Test {
    private String name;
    private String value;
    }

How to create a new Set by grouping the Set based on value and concatenate the name into a string using Java Stream?

3 Answers

Using toMap with merging (additionally stream elements are before sorted by Name):

Set<Test> result = s.stream()
                    .sorted((t1,t2)-> t1.getName().compareTo(t2.getName())) 
                    .collect(Collectors.toMap(Test::getValue, 
                                              Function.identity(),
                                              (t1,t2)-> new Test(t1.getName()+","+t2.getName(),
                                                                 t1.getValue())))
                                       .values()
                                       .stream()
                                       .collect(Collectors.toSet());

You can do something like this:

var result = setOfTests.stream()
    .collect(
        Collectors.collectingAndThen(
            Collectors.groupingBy(Test::getValue,
                // for each group, map each element to their name and join
                Collectors.mapping(Test::getName, Collectors.joining(", "))
            ), // at this point we have a Map<String, String> mapping from value to joined name
            // now we turn each entry in the map back to a Test object
            // "getValue" below refers to Map.Entry.getValue, not Test.getValue
            map -> map.entrySet().stream().map(x -> new Test(x.getValue(), x.getKey()))
                .collect(
                    Collectors.toList() // or toSet or whatever you like
                )
        )
    );
// result is a List<Test>

Note that the order of the joined names is undefined if your set is unordered, so it could be either "Test, Test 3," or "Test 3, Test,".

You could use

String testValues = tests.stream()
                .map(Test:: getValue)
                .collect(Collectors.joining(","));

You can use the below sample and change the function in map to get either the values or the keys

public static void main(String[] args) {
        Set<Test> tests = new HashSet<>();
        tests.add(new Test("First", "1st"));
        tests.add(new Test("Second", "2nd"));
        tests.add(new Test("Third", "3rd"));
        tests.add(new Test("Fourth", "4th"));
        System.out.println(tests);
        String testValues = tests.stream()
                .map(Test:: getValue)
                .collect(Collectors.joining(","));
        System.out.println(testValues);

    }

You can modify your test class as below

    public class Test {
        public Test(String name, String value) {
            this.name = name;
            this.value = value;
        }

        private String name;
        private String value;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }

        @Override
        public String toString() {
            final StringBuilder sb = new StringBuilder("Test{");
            sb.append("name='").append(name).append('\'');
            sb.append(", value='").append(value).append('\'');
            sb.append('}');
            return sb.toString();
        }
    }

Important : This doesn't guarantee the the same order of insertion

Related