Reputation: 133
Assuming that there are three variables, "staffId", "subject" and "sessionTime"
and the examples are:
<9600001, info2001, Tue3>,
<9600001, info2002, Wed4>,
<9600002, info2001, Wed2>,
<9600001, info2001, Thu9>,
<9600001, info2003, Fri10>,
<9600002, info2004, Wed4>
Finally the output should be:
STAFF_ID HOURS
9600002 4
9600001 7
Calculation: Observe that staff 9600001 has 4 actual sessions, and is involved in 3 different subjects, so this makes 4+3=7 hours in total.
To code this question, I decided to use nested HashMap, like
Map<Integer, Map<String, String>> submission = new HashMap<Integer, TreeMap<String, String>>();
but the problem was that, the tree-map replaces its previous value as the value has to be unique. For example, when I put
<9600001, info2001, Tue3> and <9600001, info2001, Thu9> into the map,
in the hash-map, there should be
9600001, info2001 - Tue3 and Thu9
but the previous value is replaced so there is only one value existing:
9600001, info2001 - Thu9
Can someone please tell me how to approach to this question?
Which data structures should I use?
Upvotes: 1
Views: 153
Reputation: 59
Though the counting formula is a bit strange (count distinct subject + count sessionTime), I guess what you meant was Map<Integer, Map<String, List<String>>>
. I've come up with 2 methods, please modify if necessary.
public class Dummy {
public Map<Integer, Integer> countHours(List<Submission> submissions) {
Map<Integer, Integer> res = new HashMap<>();
Map<Integer, Set<String>> subjectMap = new HashMap<>();
Map<Integer, List<String>> sessionTimeMap = new HashMap<>();
for (Submission s : submissions) {
subjectMap.computeIfAbsent(s.getStaffId(), v -> new HashSet<>()).add(s.getSubject());
sessionTimeMap.computeIfAbsent(s.getStaffId(), v -> new ArrayList<>()).add(s.getSessionTime());
}
for (Map.Entry<Integer, Set<String>> e : subjectMap.entrySet()) {
res.put(e.getKey(), e.getValue().size());
}
for (Map.Entry<Integer, List<String>> e : sessionTimeMap.entrySet()) {
res.merge(e.getKey(), e.getValue().size(), Integer::sum);
}
return res;
}
public Map<Integer, Integer> countHours2(List<Submission> submissions) {
Map<Integer, Map<String, Long>> collect = submissions.stream()
.collect(Collectors.groupingBy(Submission::getStaffId,
Collectors.groupingBy(Submission::getSubject, Collectors.counting())));
return collect.entrySet().stream()
.collect(Collectors.toMap(Map.Entry::getKey,
e -> e.getValue().size() +
e.getValue().values().stream().mapToInt(Number::intValue).sum()));
}
@Test
public void test() {
List<Submission> submissions = new ArrayList<>();
submissions.add(new Submission(9600001, "info2001", "Tue3"));
submissions.add(new Submission(9600001, "info2002", "Wed4"));
submissions.add(new Submission(9600002, "info2001", "Wed2"));
submissions.add(new Submission(9600001, "info2001", "Thu9"));
submissions.add(new Submission(9600001, "info2003", "Fri10"));
submissions.add(new Submission(9600002, "info2004", "Wed4"));
System.out.println(countHours(submissions)); // {9600002=4, 9600001=7}
System.out.println(countHours2(submissions)); // {9600002=4, 9600001=7}
}
}
// In case you have lombok
@Data
@AllArgsConstructor
class Submission {
private int staffId;
private String subject;
private String sessionTime;
// In case you do not have lombok
// Submission(int staffId, String subject, String sessionTime) {
// this.staffId = staffId;
// this.subject = subject;
// this.sessionTime = sessionTime;
// }
//
// public int getStaffId() {
// return staffId;
// }
//
// public void setStaffId(int staffId) {
// this.staffId = staffId;
// }
//
// public String getSubject() {
// return subject;
// }
//
// public void setSubject(String subject) {
// this.subject = subject;
// }
//
// public String getSessionTime() {
// return sessionTime;
// }
//
// public void setSessionTime(String sessionTime) {
// this.sessionTime = sessionTime;
// }
}
Upvotes: 0
Reputation: 27996
Java is an object oriented language so you are best off making classes to represent the items you want to record and manipulate. In your case there are staff, subjects and sessions:
class Staff {
private final String id;
}
class Subject {
private final String id;
}
class Session {
private final Staff staff;
private final Subject subject;
int period;
}
If you have a list of sessions then you can fairly easily generate a map from staff to sum of period (for example):
List<Session> sessions;
Map<Staff, Integer> staffTotalTime = sessions.stream()
.collect(groupingBy(Session::getStaff, summingInt(Session::getPeriod));
My recommendation is to not start by modelling data to optimise access. Form data structures that are a simple and elegant representation of the relationships between the objects and then manipulate it into other forms when required. It would be trivial to change the statement above to sum by subject or create an average or just about anything else without having to change the data model you use to store the objects.
Upvotes: 0
Reputation: 2751
You need to create a custom object and a Map
for this problem.
Map<Integer, CustomObject> map = new HashMap<Integer, CustomObject>();
and
class CustomObject {
int staffIdCount; //Number of distinct staffId records (e.g. for 9600001 value will be 4)
Set<String> subject = new HashSet<String>(); //distinct subjects
}
Functionality will be like below:
when you encounter 9600001
perform below operation
if(map.containsKey(9600001))
{
CustomObject tmp = map.get(9600001);
tmp.staffIdCount+=1;
tmp.subject.add("info2001"); //the corresponding subject
map.put(9600001, tmp);
}
else
{
CustomObject tmp = new CustomObject();
tmp.staffIdCount +=1;
tmp.subject.add("info2001");
map.put(9600001, tmp);
}
EDIT: so staffIdCount
will give you number of staffId
s and size of subject
will give you distinct count of subject
Upvotes: 0
Reputation: 12243
Use List
of records.
class Record {
int staffId;
String subject;
String sessionTime;
}
List<Record> submissions = new ArrayList<>();
or Map
of List
class Record {
String subject;
String sessionTime;
}
Map<Integer, List<Record>> submissions = new HashMap<>();
Upvotes: 3