2

I have the following code which group the users with same age and highest score among them. I have now instead of Map<Person, List<String>> one object called Person and in the person class there Map Map<String,Double> nameScoreTogether;. I need to store the output in the map of the person object with its informations(name and the corresponding score), so how can i change the code accordingly ?

Input: In data type of Map<Person, String>:

    {Person has at Age: 12 (Score: 50)
    =alex,
     Person has at Age: 16 (Score: 50)
    =miki, 
    Person has at Age: 5 (Score: 100)
    =shi, 
    Person has at Age: 4 (Score: 50)
    =rafi, 
    Person has at Age: 1 (Score: 50)
    =sharbel, 
    Person has at Age: 5 (Score: 0)
    =thomas, 
    Person has at Age: 14 (Score: 60)
    =thomy, 
    Person has at Age: 14 (Score: 50)
    =angelos,
     Person has at Age: 11 (Score: 50)
    =musti, 
    Person has at Age: 11 (Score: 100)
    =aloo,
     Person has at Age: 2 (Score: 50)
    =evi}  

The Expected output is:

    Person(score=50.0, age=1) - [sharbel=50.0]
    Person(score=100.0, age=11) - [aloo=100.0, musti=50.0]
    Person(score=50.0, age=12) - [Alex=50.0]
    Person(score=60.0, age=14) - [thomy=60.0, angelos=50.0]
    Person(score=50.0, age=2) - [evi=50.0]
    Person(score=100.0, age=5) - [shi=100.0, Thomas=5.0]
    Person(score=50.0, age=4) - [rafi=50]
    Person(score=50.0, age=16) - [miki=50]

Try code: but now I have List<Person> which has Map<String,Double>

    Map<Person, List<String>> result = origin.entrySet().stream()
        .collect(Collectors.groupingBy(e -> e.getKey().getAge())).entrySet().stream()
        .collect(Collectors.toMap(
            e -> e.getValue().stream()
                .map(Map.Entry::getKey)
                .max(Comparator.comparing(Person::getScore))
                .get(),
            e -> e.getValue().stream()
                .map(Map.Entry::getValue)
                .collect(Collectors.toList()))
        );

Class Person:

    public class Person {
        int Age;
        int lineScoreMax;
        Map<String, Double> nameScoreTogether;
    }
8
  • If I understand you have Map<Person, String> and you want to transform it to Map<Person, List<String>> ? Commented Jun 11, 2020 at 10:44
  • No i want to transform it to List<Person> Commented Jun 11, 2020 at 11:35
  • So each person has lineScore which is the heighst score and includes list of tuple person name and its score Commented Jun 11, 2020 at 11:40
  • Can you please fix your inputs and the expected output? Person has at Age: 1 (Score: 50). (Score: 50) =sharbel, Why there are two Score in this line? Commented Jun 11, 2020 at 11:45
  • Updated the post Commented Jun 11, 2020 at 14:05

3 Answers 3

1
+50

I have changed the Person class as

public class Person {
    int age;
    double lineScoreMax;
    Map<String, Double> nameScoreTogether;

    Person(int age, double score) {
        this.age = age;
        this.lineScoreMax = score;
    }

    Person(int age, double score,  Map<String, Double> nameScoreTogether) {
        this.age = age;
        this.lineScoreMax = score;
        this.nameScoreTogether = nameScoreTogether;
    }

    public int getAge() {
        return age;
    }

    public double getLineScoreMax() {
        return lineScoreMax;
    }
}

I have added a new three parameter constructor.

Map<Person, String> origin = new HashMap();
//Populate origin

Map<Integer, List<Map.Entry<Person, String>>> ageToListOfPersonNames = origin.entrySet()
            .stream()
            .collect(Collectors.groupingBy(entry -> entry.getKey().getAge()));

List<Person> persons = ageToListOfPersonNames.entrySet()
        .stream()
        .map(entry -> new Person(entry.getKey(),
                //Find the max score for an age
                entry.getValue()
                     .stream()
                     .map(Map.Entry::getKey)
                        .max(Comparator.comparingDouble(Person::getLineScoreMax))
                        .map(Person::getLineScoreMax)
                        .orElse(0D),

                //Find the map of name to score
                entry.getValue()
                        .stream()
                        .collect(Collectors.toMap(Map.Entry::getValue,
                                personNameEntry -> personNameEntry.getKey().getLineScoreMax()))
            ))
        .collect(Collectors.toList());

First, I map the input to a map of age to list of person, name entries.

Next, I stream it and compute the max score and map of person name to individual score and pass them as arguments to the Person constructor. Finally, I collect the individual Person objects as a list.

Note: This assumes that person name is unique.


UPDATE 1: From your comment,

[..] what if i have in person class instead of Map<String, Double> nameScoreTogether; a list of object like this: List<Information> nameScoreTogether and Information includes an attribute called String:name, and attribute called double:age

The Person class changes as

public class Person {
    int age;
    double lineScoreMax;
    List<Information> nameScoreTogether;

    Person(int age, double score) {
        this.age = age;
        this.lineScoreMax = score;
    }

    Person(int age, double score,  List<Information> nameScoreTogether) {
        this.age = age;
        this.lineScoreMax = score;
        this.nameScoreTogether = nameScoreTogether;
    }
    //Getters
}

Information class:

private class Information {
    private String name;
    private double score;

   Information(String name, double score) {
        this.name = name;
        this.score = score;
    }
}

Use the below block in place of //Find the map of name to score in the earlier provided code.

entry.getValue()
     .stream()
     .map(personNameEntry -> new Information(personNameEntry.getValue(),
                            personNameEntry.getKey().getLineScoreMax()))
     .collect(Collectors.toList())

UPDATE 2:

If Information class only has a single argument constructor and a setter for score, change the map step as

.map(personNameEntry -> {
    Information information = new Information(personNameEntry.getValue());
    information.setScore(personNameEntry.getKey().getLineScoreMax());
    return information;
  })
Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for your solution: i have a question please: what if i have in person class instead of Map<String, Double> nameScoreTogether; a list of object like this: List<Information> nameScoreTogether and Information includes an attribute called String:name, and attribute called double:age Could you please added this variant under your solution above?
@Catalina See my edit. I have given only the part to compute the third argument of Person class. The rest of the code remains the same.
Thanks, sorry but i forgot to mention that the score of Information is accessing only through a setter method. we can only create an object of class inforamtion like this: new Information("name") and to set the score we have to use setter mthode. setScore()
That will be really my last change request
I have shown how to change the map block. But having a constructor with two params is better and neat. In the future avoid asking multiple questions. It is not advised here.
|
0

Try this solution.

List<Person> persons = new ArrayList<>();
    personAndNameMap.entrySet().stream()
            // collect the list of persons group by age (it collect list of persons with same age)
            .collect(Collectors.groupingBy(e -> e.getKey().getAge()))
            // iterate the persons of same age
            .forEach((key, value) -> {
                // set the best score to 0 among them
                double bestScore = 0;
                // create a map to store name and their line score
                Map<String, Double> nameAndScoreMap = new HashMap<>();
                for (Map.Entry<Person, String> map : value) {
                    // check and update best score
                    bestScore = Math.max(bestScore, map.getKey().getLineScoreMax());
                    // add person name and his line score in map
                    nameAndScoreMap.put(map.getValue(), map.getKey().getLineScoreMax());
                }
                // add the person in list
                persons.add(new Person(key, bestScore, nameAndScoreMap));
            });

for above solution create this constructor in Person class

public Person(int age, double lineScoreMax, Map<String, Double> nameAndScoreMap) {
    this.age = age;
    this.lineScoreMax = lineScoreMax;
    this.nameAndScoreMap = nameAndScoreMap;
}

1 Comment

I considered that min score of a person could be 0. If -ve score is allowed then initialize the bestScore to Double.MIN_VALUE.
0

If you want a simple approach to solve your problem the go through the following snap code

// class 
class Person{
    int age;
    double linescoreMax;
    Map<String, Double> nameScoreTogether;

// setter getter

}

Result Map as per your requirement You can convert these simple line of code into lambda expression very easily if

        Map<Integer, Person> resultMap = new HashMap<>();

//      Repeat for each inputs :  inputName, inputAge, inputScore

        if (resultMap.containsKey(inputAge)) {
            Person person = resultMap.get(inputAge);
            if (inputScore > person.getLinescoreMax()) {
                person.setLinescoreMax(inputScore);
            }
            person.getNameScoreTogether().put(inputName, inputScore);
        } else {
            Person p = new Person();
            Map<String, Double> pMap = new HashMap<>();
            pMap.put(inputName, inputScore);

            p.setAge(inputAge);
            p.setLinescoreMax(inputScore);
            p.setNameScoreTogether(pMap);

            resultMap.put(p.getAge(), p);
        }

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.