fill an HashMap with elements of a set passed as parameter - java

i have to write the method:
public Map<Robot, Integer> PickedUpForEachRobot(Set<Stuff> pickedUp)
which has to iterate through the set passed as parameter and has to count the quantity of stuff picked up by each robot and associate it to his instance.
what i have done is this:
public Map<Robot, Integer> PickedUpForEachRobot(Set<Stuff> pickedUp) {
final Map<Robot,Integer> map = new HashMap<>();
for(Stuff stuff : pickedUp){
Integer quantity = map.get(stuff.getPicker());
if(quantity!=null){
map.put(stuff.getPicker(), quantity);
}
}
return map;
}
I also have other classes:
public class Stuff {
private Robot picker;
public Robot getPicker() {
return this.picker;
}
}
and:
public class Robot {
private Set<Stuff> bunchOfStuff;
public Set<Stuff> getBunchOfStuff() {
return this.bunchOfStuff;
}
}
for which i have tried to be synthetic, so i hope i can be clear anyway.
So my problem is that when i do a test for this method:
#Test
public void testRaccoltoPerMezzo() {
Statistics stats = new Statistics();
Stuff stuff1 = new ball();
Stuff stuff2 = new legoPiece();
Set<Stuff> set = new HashSet<>();
set.add(stuff1);
assertEquals(1,set.size());
Map<Robot,Integer> map = new HashMap<>();
map.put(stuff1.getPicker(),1);
assertEquals(map, stats.PickedUpForEachRobot(set));
}
it fails and it says to me:
java.lang.AssertionError: expected:<{null=1}> but was:<{}>
and i can't understand why. Can somebody help me?

This message :
java.lang.AssertionError: expected:<{null=1}> but was:<{}>
means that you expect to have a map with one element that owns a null key and as associated value 1 but you got a empty map.
The expected map you have created doesn't seem to be adequate according to your requirement and the actual map either.
About the populating of the map in the implementation, I noticed at least this point that is not at all logical.
Here :
final Map<Robot,Integer> map = new HashMap<>();
for(Stuff stuff : pickedUp){
Integer quantity = map.get(stuff.getPicker());
if(quantity!=null){
map.put(stuff.getPicker(), quantity);
}
}
Integer quantity = map.get(stuff.getPicker()); will always value quantity to null as you get it from an empty map : map = new HashMap<>(); and you populate the map only if quantity is not null :
if(quantity!=null){
map.put(stuff.getPicker(), quantity);
}
But it will never happen as the map is empty : so you never populate the map.
You have probably other issues in the code but I hope it will help you to rework your logic.

Related

Java - JSON output is not expected as what console suggests

I'm new to Java and writing APIs.
I basically have two things: a HashMap called db that should be returned as a JSON and an ArrayList called defaultParameters. Basically what the application does are the following:
db basically contains an array of objects of key-value pairs that should be returned as a JSON when a user makes a GET request to this address.
defaultParameters is basically a list of default key-value pairs. If there is no key-value pair within that object, then that object takes in that default key-value pair.
I was able to get it to display on the console, but for some reason, the updated values are not appearing in the JSON when I do the get request.
Here are the relevant code snippets:
private static ArrayList<Item> DB = new ArrayList<>();
private static HashMap<String, String> defaultValues = new HashMap<>();
private void updateAllItems(){
for(Item item : DB){
for(Map.Entry entry : defaultValues.entrySet()){
String currentField = (String) entry.getKey();
String currentValue = (String) entry.getValue();
item.addField(currentField, currentValue);
}
}
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getAllItems() {
updateAllItems();
for(Item item : DB){
// Test code that I added
item.printItem();
}
return Response.ok(DB).build();
}
Snippets of the Item class
public class Item {
private HashMap<String, String> item = new HashMap<>();
public void addField(String key, String value){
item.put(key, value);
}
public void printItem(){
for(Map.Entry entry : item.entrySet()){
String currentField = (String) entry.getKey();
String currentValue = (String) entry.getValue();
System.out.println(currentField + ": " + currentValue);
}
}
}
Doing the POST request and doing the GET request yields the following:
On the console (Something: notsomething) is new:
seller: Mrs. Fields
price: 49.99
title: Cookies
category: 42
something: notsomething
The JSON response however:
[{"category":"42","seller":"Mrs. Fields","price":"49.99","title":"Cookies"}]
The JSON is missing the new key-value pair that the console has. I'm trying to have the JSON reflect what the console is doing. Anyone have any ideas?
Alright, after some thinking, I figured out what to do.
I changed my code from
public class Item {
to
public class Item extends HashMap<String, String> {
and removed
private HashMap<String, String> item = new HashMap<>();
which means I had to change item to this. I figured since that I'm going to be using each instance as a hashmap, I may as well extend the hashmap that will change the instance of the item too.
Thanks for everyone's help. The comment gave me some more insight of what I was trying to do which led to a solution.

map inside a map - Need data in specific format

I have a requirement where I want to read multiple project pom files and display data in below format
{
"java" : {"1.7" : ["project1"],"1.8": ["project2"]},
"junit" : {"4.12" : ["project1"]},
"hsqldb" : {"1.8" : ["project3"],"1.1": ["project6"]}
}
My coding is getting input on project , ver and technlogy and displaying, but however I couldnt second value inside the internal
private void addTechnologyData(String projName,String techName,String ver)
{
String keyFormat=techName;
if (technologyMap.containsKey(keyFormat)) {
Map preValue=technologyMap.get(keyFormat);
if(!preValue.containsValue(projName)) {
Map<String,String> temp = new HashMap();
temp=preValue;
temp.put(ver,projName);
technologyMap.put(keyFormat, temp);
}
} else {
Map<String,String> projectVersiomap = new HashMap();
projectVersiomap.put(ver,projName);
technologyMap.put(keyFormat, projectVersiomap);
}
}
Please help me understand why I couldnt add 2nd key value pair to Internal map?
Is there a better way than what Im doing?
If my understanding is correct, you are expecting more than one project per version (since you have an array), so your Map is preventing this, you can only have one value per key. You can use a List like Map<String,Map<String, List<String>>> but I would suggest to use some POJO to keep the code cleaner.
Create a Technology class that will hold a list of Project for each version in a Map. This would look like :
public class Technology{
private String name;
private Map<String, List<Project>> projects;
public Technology(String name){
this.name = name;
this.projects = new HashMap<>();
}
public void addProjectToVersion(String version, Project project){
List<Project> l = this.projects.get(version);
if(l == null){ //doesn't exist
l = new ArrayList<>();
this.projects.put(version, l);
}
l.add(project);
}
}
Here, the logic is in the POJO. You just need to use a Collection to hold your instance, a Map if you like, or a List (implement equals and hashCode to recover easily the instance). And you can use it like :
private Map<String, Technology> techs = new HashMap<>();
....
public void addProject(String tech, String version, String projectName){
Technology t = techs.get(tech);
if(t == null){ //doesn't exist
t = new Technology(tech);
techs.put(tech, t);
}
t.addProjectToVersion(version, new Project(projectName));
}
public void insertData(){
addProject("java", "1.7", "project1");
addProject("java", "1.7", "project2");
addProject("java", "1.8", "project1");
addProject("junit", "4.12", "project1");
System.out.println(techs);
}
This will input correctly :
{junit={4.12=[project1]}, java={1.7=[project1, project2], 1.8=[project1]}}
Note that I reused your logic, based on the requirements it could be better to have a List<Project> holding each Technology with the version. But this is based on the context.

Remove duplicates based on a few object properties from list

I have a List collection where each Metric contains several properties such as: metricName, namespace, fleet, type, component, firstSeenTime, lastSeenTime, etc. There are duplicates in this list such that all properties are same except for firstSeenTime and lastSeenTime. I am looking for an elegant way to filter this list and only return the metrics with the most recent lastSeenTime when there are such duplicates.
Something better than this:
private List<Metric> processResults(List<Metric metrics) {
List<Metric> results = new ArrayList<>();
for (Metric incomingMetric: metrics) {
// We need to implement "contains" below so that only properties
// other than the two dates are checked.
if (results.contains(incomingMetric) {
int index = results.indexOf(incomingMetric);
Metric existing = results.get(index);
if (incomingMetric.getLastSeen().after(existing.getLastSeen())) {
results.set(index, metricName);
} else {
// do nothing, metric in results is already the latest
}
} else {
// add incomingMetric to results for the first time
results.add(incomingMetric);
}
}
return results;
}
The results.contains check is done by iterating over all the Metrics in results and checking if each object matches the properties except for the two dates.
What could be a better approach that this for both elegance and performance?
In java the most elegant way to compare things is the Comparator interface. You should remove the duplicates using something like:
public List<Metric> removeDuplicates(List<Metric> metrics) {
List<Metric> copy = new ArrayList<>(metrics);
//first sort the metrics list from most recent to older
Collections.sort(copy, new SortComparator());
Set<Metric> set = new TreeSet<Metric>(new Comparator<Metric>() {
#Override
public int compare(Metric o1, Metric o2) {
int result = 0;
// compare the two metrics given your rules
return result;
}
});
for(Metric metric : copy) {
set.add(metric);
}
List<Metric> result = Arrays.asList(set.toArray());
return result;
}
class SortComparator implements Comparator<Metric> {
#Override
public int compare(Metric o1, Metric o2) {
int result = 0;
if(o2.getLastSeenTime() != null && o1.getLastSeenTime() != null) {
result = o2.getLastSeenTime().compareTo(o1.getLastSeenTime());
}
return result;
}
}
The strong of this approach is that you could write a family of comparators and provide a Factory to choose at runtime the best way to compare your metrics and remove or not instances as duplicates among the runtime conditions:
public void removeDuplicates(List<Metric> metrics, Comparator<Metric> comparator) {
List<Metric> copy = new ArrayList<>(metrics);
Collections.sort(copy, new SortComparator());
Set<Metric> set = new TreeSet<Metric>(comparator);
for(Metric metric : copy) {
set.add(metric);
}
List<Object> result = Arrays.asList(set.toArray());
return result;
}
I’m not sure how you are generating List<Metric>. But if you can maintain a Map<String, Metric> instead of that list you may can try the below approach.
So the key of this map will be a combination of all these values you need to compare. (except the date attributes.)
Key: “{metricName}${type}$.....”
For this you can maintain another attribute in Metric object with getter. When you call the getter it will return the key.
Then check the key is exist or not before you put into the map. If it’s exist, get the stored Metric in map for that key and do the date comparison to find the latest Metric object. If it’s the latest replace the map's stored object with new object.
PS : Do the execution time comparison for both cases. So you will find the best approach.
Thanks for the answers. I went with the map approach since it does not incur additional sorts and copies.
#VisibleForTesting
Set<Metric> removeDuplicates(List<Metric> metrics) {
Map<RawMetric, Metric> metricsMap = new HashMap<>();
for (Metric metric : metrics) {
RawMetric rawMetric = RawMetric.builder()
.metricName(metric.getName())
.metricType(metricName.getMetricType())
... // and more
.build();
// pick the latest updated metric (based on lastSeen date)
BiFunction<RawMetric, Metric, Metric> biFunction =
(k, v) -> Metric.builder()
.name(k.getMetricName())
.metricType(k.getMetricType())
... // and more
.lastSeen(v.getLastSeen().after(
metricName.getLastSeen()) ? v.getLastSeen() :
metricName.getLastSeen())
.firstSeen(v.getFirstSeen())
.build();
metricsMap.putIfAbsent(rawMetric, metric);
metricsMap.computeIfPresent(rawMetric, biFunction);
}
return ImmutableSet.copyOf(metricsMap.values());
}
#Value
#Builder
static class RawMetricName {
private String metricName;
private String metricType;
private String ad;
private String project;
private String fleet;
private String host;
private int granularity;
}

Java Enum group enums by their values

Can you help me with solving some thing. I have enum in my java application:
public enum EnumKey {
ALPHA("group1"),
BETA("group2"),
GAMMA("group3"),
SIGMA("group2"),
DELTA("group2")
// and other values with "group1" or "group2" or "group3", constructors and getters
private String groupName;
}
I want to get something like Map<String, List<EnumKey>>, where keys is my specified groupNames and values is groupped and sorted by groupName enumKey list(I have only three specified groupNames). I hope my question is clear.
Hope you will help me to find an elegant way to do this.
This should generate the map you need:
public static Map<String, List<EnumKey>> crateEnumKeyMap() {
Map<String, List<EnumKey>> map = new HashMap<>();
for (EnumKey enumKey : EnumKey.values()) {
List<EnumKey> enumKeyList = map.get(enumKey.getGroupName());
if (enumKeyList == null) {
enumKeyList = new ArrayList<>();
map.put(enumKey.groupName, enumKeyList);
}
enumKeyList.add(enumKey);
}
return map;
}
Just create a map inside your EnumKey with the signature you yourself suggested.
In a static code block iterate over all the values in the enum and add them to the map by adding them to the appropriate list if it exists, if not creating the list and then adding it.

How to add a DynamoDB scan result to an object list?

I am performing a scan on a DynamoDB table and I need to then add respective attributes from the returned items to a list of type User (User has a single constructor User(String uuid)). The code currently successfully scans the DB and returns a List of the scan results. However my iteration seems to return null for some reason.
AmazonDynamoDBClient client = dynamoClient.getDynamoClient();
DynamoDBMapper mapper = new DynamoDBMapper(client);
try {
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
Map<String, Condition> scanFilter = new HashMap<String, Condition>();
Condition scanCondition =
new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL);
scanFilter.put("uuid", scanCondition);
scanExpression.setScanFilter(scanFilter);
List scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (Iterator it = scanResults.iterator(); it.hasNext();) {
//User user = (User) it.next();
allUserSummary.add(new User(scanResults.get(1).toString()));
}
} catch (Exception e) {
// TODO
}
I suggest you start using the modern and compact list iteration by means of The For-Each Loop, which helps to avoid many common errors when using the old iteration style:
[...]
The iterator is just clutter. Furthermore, it is an opportunity for
error. The iterator variable occurs three times in each loop: that is
two chances to get it wrong. The for-each construct gets rid of the
clutter and the opportunity for error. Here is how the example looks
with the for-each construct:
void cancelAll(Collection<TimerTask> c) {
for (TimerTask t : c)
t.cancel();
}
Applying this to your use case yields the following approximately:
List<UserAccounts> scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (UserAccounts userAccounts : scanResults) {
allUserSummary.add(new User(userAccounts.toString()));
}
In case this doesn't work already, it could hint towards the actual error as well, insofar your code assumes the toString() of class UserAccounts to return the uuid, which may or may not be the case. The usual approach is to have a getKey() or getUuidAttribute() method and respective annotations #DynamoDBHashKey or #DynamoDBAttribute, as shown in the example for Class DynamoDBMapper, e.g.:
#DynamoDBTable(tableName = "UserAccounts")
public class UserAccounts{
private String key; // or uuid right away
#DynamoDBHashKey
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
// ...
}
This would obviously yield the following for your example:
List<UserAccounts> scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (UserAccounts userAccounts : scanResults) {
allUserSummary.add(new User(userAccounts.getKey()));
}

Categories