[FIXED] how to store the values that has the same parent key

Issue

lets say I have:

bob:V
bob:A
bob:B
bob:C
bob:C
sally:B
sally:C
sally:A
steve:A
steve:B
steve:C

how do I store:

the values as:

bob={V,A,B,C,C}, sally={B,C,A}, steve={A,B,C}

and for any guy who has a sequence A,B,C repeated how do I get that person name?

I am fairly new to Java and Im trying to implement this scenario, as I dont see anything like this in this communtiy.

here is my final answer: first stored the list into a map and then used collectors to loop through and map it to their respective attributes.

public class Solution{

static List<String> doWork(List<LogItem> eventsInput) {
        Map<String, String> personMap = eventsInput.stream()  
                .collect(Collectors.toMap(LogItem::getUserId, p -> Character.toString(p.getEventChar()), String::concat));
        
        System.out.println("person map is \n" + personMap);
        
        BiPredicate<Entry<String, List<String>>, String> contains =
                (entry, attr) -> entry.getValue().stream()
                        .collect(Collectors.joining()).contains(attr);
                String attributes = "ABC";
                List<String> results = personMap.entrySet().stream()
                        .filter(e -> e.getValue().contains(attributes))
                        .map(Entry::getKey).collect(Collectors.toList());
                return results;
    }

public static void main(String[] args){
List<LogItem> exampleInputItems = new ArrayList<>();
        exampleInputItems.add(new LogItem("bob", 'V'));
        exampleInputItems.add(new LogItem("bob", 'A'));
        exampleInputItems.add(new LogItem("steve", 'A'));
        exampleInputItems.add(new LogItem("bob", 'B'));
        exampleInputItems.add(new LogItem("bob", 'C'));
        exampleInputItems.add(new LogItem("bob", 'C'));
        exampleInputItems.add(new LogItem("steve", 'B'));
        exampleInputItems.add(new LogItem("sally", 'B'));
        exampleInputItems.add(new LogItem("steve", 'C'));
        exampleInputItems.add(new LogItem("sally", 'C'));
        exampleInputItems.add(new LogItem("sally", 'A'));

        
        List<String> returnedNames = doWork(exampleInputItems);

        if (returnedNames.size() != 2) {
            throw new RuntimeException("Wrong number of names found. Found: " + returnedNames);
        }
        if (!returnedNames.contains("bob")) {
            throw new RuntimeException("Did not find \"bob\" in the returnedNames: " + returnedNames);
        }
        if (!returnedNames.contains("steve")) {
            throw new RuntimeException("Did not find \"steve\" in the returnedNames: " + returnedNames);
        }

        System.out.println("The example passed.");
    }
static class LogItem {
    public String userId;
    public char eventChar;
    public LocalDateTime dateTime;

    LogItem(String userId, char eventChar) {
        this.userId = userId;
        this.eventChar = eventChar;
        dateTime = LocalDateTime.now();
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public char getEventChar() {
        return eventChar;
    }

    public void setEventChar(char eventChar) {
        this.eventChar = eventChar;
    }

    public LocalDateTime getDateTime() {
        return dateTime;
    }

    public void setDateTime(LocalDateTime dateTime) {
        this.dateTime = dateTime;
    }

    @Override
    public String toString() {
        return "LogItem [userId=" + userId + ", eventChar=" + eventChar + ", dateTime=" + dateTime + ", getUserId()="
                + getUserId() + ", getEventChar()=" + getEventChar() + ", getDateTime()=" + getDateTime() + "]";
    }
    

}

}
}

Solution

First, I would store the attributes in a Map<String,String>. This will make it easier to filter the attributes later. I am using a record in lieu of a class but a class would work as well.

record Person(String getName, String getAttribute) {
}

Create the list of Person objects

List<Person> list = List.of(new Person("bob", "V"),
        new Person("bob", "A"), new Person("bob", "B"),
        new Person("bob", "C"), new Person("bob", "C"),
        new Person("sally", "B"), new Person("sally", "C"),
        new Person("sally", "A"), new Person("steve", "A"),
        new Person("steve", "B"), new Person("steve", "C"));

Now create the map. Simply stream the list of people and concatenate the attributes for each person.

Map<String, String> personMap = list.stream()
        .collect(Collectors.toMap(Person::getName,
                Person::getAttribute, String::concat));

The map will look like this.

bob=VABCC
steve=ABC
sally=BCA

Now grab the name based on an attribute string.

Now stream the entries of the map and pass the entry whose value contains the attribute string. Then retrieve the key (name) and return as a list of names.


String attributes = "ABC";
ListString> results = personMap.entrySet().stream()
        .filter(e -> e.getValue().contains(attributes))
        .map(Entry::getKey).collect(Collectors.toList());

System.out.println(results);

prints

[bob, steve]

Alternative approach using Map<String, List<String>>

Group the objects by name but the values will be a list of attributes instead of a string.

Map<String, List<String>> personMap = list.stream()
        .collect(Collectors.groupingBy(Person::getName,
                Collectors.mapping(Person::getAttribute,
                        Collectors.toList())));

The map will look like this.

bob=[V, A, B, C, C]
steve=[A, B, C]
sally=[B, C, A]

To facilitate testing the attributes, a BiPredicate is used to stream the list and concatinate the attributes and then check and see if it contains the attribute string.

BiPredicate<Entry<String, List<String>>, String> contains =
        (entry, attr) -> entry.getValue().stream()
                .collect(Collectors.joining()).contains(attr);

As before, stream the entry set of the map and apply the filter to pass those entries which satisfy the condition. In this case, the filter invokes the BiPredicate.

String attributes = "ABC";
List<String> results = personMap.entrySet().stream()
        .filter(e->contains.test(e, attributes))
        .map(Entry::getKey).collect(Collectors.toList());

System.out.println(results);

prints

[bob, steve]
 

Update Answer

To work with Character attributes, you can do the following using the first example.

Map<String, String> personMap2 = list.stream()
                .collect(Collectors.toMap(Person::getName,
                        p -> Character.toString(p.getAttribute()),
                        String::concat));
        

Imo, it would be easier, if possible to change your attribute types to string.

Answered By – WJS

Answer Checked By – Willingham (Easybugfix Volunteer)

Leave a Reply

(*) Required, Your email will not be published