How to deal with same keys in HashMaps ? - java

Hello fellow soldiers.
Obviously keys in hashmaps are unique. However, I've been trying to write a code that reads a csv file and then puts the key and value in the map. However, there are keys the same (every key is like 15 times in the csv file). In that case, it should make a sum of the values, and just return the key once.
How to do that? My code right now is as follows.
BufferedReader br = null;
String line;
try {
br = new BufferedReader(new FileReader(filepath));
} catch (FileNotFoundException fnfex) {
System.out.println(fnfex.getMessage() + "Bestand niet gevonden!");
System.exit(0);
}
//this is where we read lines
try {
while((line = br.readLine()) != null) {
String[] splitter = line.split(cvsSplitBy);
if(splitter[0] != "Voertuig") {
alldataMap.put(splitter[0], splitter[8]);
}
//MIGHT BE JUNK, DONT KNOW YET
/*if((splitter[0].toLowerCase()).contains("1")){
double valuekm = Double.parseDouble(splitter[8]);
license1 += valuekm;
System.out.println(license1);
}
else {
System.out.println("not found");
}*/
}
System.out.println(alldataMap);
TextOutput();
} catch (IOException ioex) {
System.out.println(ioex.getMessage() + " Error 1");
} finally {
System.exit(0);
}
So if I have the following info (in this case its the 0th and 8th word read every line in the csv file)
Apples; 299,9
Bananas; 300,23
Apples; 3912,1
Bananas;342
Bananas;343
It should return
Apples;Total
Bananas;Total

Try the following:
if( alldataMap.containsKey(splitter[0]) ) {
Double sum = alldataMap.remove(splitter[0]) + Double.parseDouble(splitter[8]);
allDataMap.put(splitter[0], sum );
} else {
alldataMap.put(splitter[0], Double.valueOf(splitter[8]) );
}

You can use putIfAbsent and compute since Java 8:
Map<String, Integer> myMap = new HashMap<>();
//...
String fruitName = /*whatever*/;
int qty = /*whatever*/;
myMap.putIfAbsent(fruitName, 0);
myMap.compute(fruitName, (k, oldQty) -> oldQty + qty);

You can use Map#containsKey() to check for an existing mapping, then if there is one use Map#get() to retrieve the value and add the new one, and finally Map#put() to store the sum:
if(map.containsKey(key))
map.put(key, map.get(key)+value);
else
map.put(key, value);
See here for the documentation of those methods.

i would use the merge for a map :
alldataMap.merge(splitter[0], Double.valueOf(splitter[8]), (oldVal, newVal) -> oldVal + newVal);
From doc:
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null. This method may be of use when combining multiple mapped values for a key. For example, to either create or append a String msg to a value mapping:

I won't suggest the way to it in a loop because that's already done, but I'd suggest a Streams solution, in a unique line :
Map<String, Double> alldataMap = new HashMap<>();
try {
alldataMap =
Files.lines(Paths.get("", filepath))
.map(str -> str.split(cvsSplitBy))
.filter(splitte -> !splitte[0].equals("Voertuig"))
.collect(Collectors.toMap(sp -> sp[0],
sp -> Double.parseDouble(sp[8].replaceAll(",", ".")),
(i1, i2) -> i1 + i2));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println(alldataMap); // {Apples=4212.0, Bananas=985.23}
The steps are the same :
Iterate over the lines
split on the cvsSplitBy
remove lines which starts with Voertuig (! use .equals() and not !=)
build the map following 3 rules :
the key is the first String
the value is second String parsed as Double
if merge is required : sum both
Edit, as nobody propose the use of .getOrDefault() I give it
while ((line = br.readLine()) != null) {
String[] splitter = line.split(cvsSplitBy);
if (!splitter[0].equals("Voertuig")) {
alldataMap.put(splitter[0],
alldataMap.getOrDefault(splitter[0], 0.0) +
Double.parseDouble(splitter[8].replaceAll(",", ".")));
}
}
If tke key already exists, it'll sum, it the key does not exists it'll sum the value with a 0

Related

How to break from forEach loop when exception occur

I have a forEach loop inside a for loop. Something like this.
for (Iterator iterator = notLoadedFiles.iterator(); iterator.hasNext();) {
SwapReport swapReport = (SwapReport) iterator.next();
Reader reader = Files.newBufferedReader(Paths.get(swapReport.getFilePath()));
CSVParser csvParser = new CSVParser(reader, CSVFormat.DEFAULT);
List<CSVRecord> initList = csvParser.getRecords();
CSVRecord csvRecord = initList.stream().filter(record -> record.get(0).endsWith("#xxx.com")).findAny().orElse(null);
if (csvRecord != null) {
// partition the list
List<List<CSVRecord>> output = ListUtils.partition(initList, chunkSize);
// Set data by slice
output.stream().forEach(slice -> {
String inParams = slice.stream().filter(record -> !record.get(0).endsWith("#xxx.com")).map(record -> "'" + record.get(0) + "'").collect(Collectors.joining(",")).toString();
try {
// Retrieve data for one slice
setAccountData(inParams);
}
} catch (SQLException e) {
// How to break ?
}
});
}
}
The forEach loop will execute the setAccount method. The try catch block inside the forEach loop is because setAccount throwing SQLException.
What I want to do is to stop(break) from the forEach loop in case there is an exception.
If there is an exception the inner loop will stop and for loop (outer loop) will proceed to the next element in the iterator.
How can I do that ?
Thank you
Sadly, function .ForEach() did not provided a good way to break the loop. Although adding a return statement inside this loop could let the .ForEach() to continue next loop, but I'm sure that's not what you are asking.
Solution
Therefore, you can try to use a feature call .takeWhile() introduced in Java 9. For example, the code will be:
boolean varNameYouPrefer = true;
output.stream().takeWhile(slice -> varNameYouPrefer).forEach(slice -> {
String inParams = slice.stream().filter(record -> !record.get(0).endsWith("#xxx.com")).map(record -> "'" + record.get(0) + "'").collect(Collectors.joining(",")).toString();
try {
// Retrieve data for one slice
setAccountData(inParams);
}
} catch (SQLException e) {
// To break, set varNameYouPrefer to false.
varNameYouPrefer = false;
}
});
References
Java Stream Document
Example of using takeWhile

Java comparing csv file values

I'm trying to create a csv file where only 1 team name is shown per row, so when you click the button twice it will only add the team name if its not already there. currently it adds the team "UWE" every single time you press the button. the code for this is below:
public void showStats(ActionEvent event){
try {
File matchFile = new File("src/sample/matchData.csv");
File scoreFile = new File("src/sample/scoreData.csv");
Scanner matchReader = new Scanner(matchFile);
Scanner scoreReader = new Scanner(scoreFile);
while (matchReader.hasNextLine()) {
String data = matchReader.nextLine();
List<String> matchList = Arrays.asList(data.split(","));
while (scoreReader.hasNextLine()) {
String dataScore = scoreReader.nextLine();
List<String> dataScoreList = Arrays.asList(dataScore.split(","));
if (dataScoreList.get(0).equals(matchList.get(0))) {
//
} else {
writeExcel("scoreData", matchList.get(0)) ;
}
System.out.println(dataScoreList);
}
System.out.println(matchList);
}
matchReader.close();
scoreReader.close();
} catch (FileNotFoundException e) {
System.out.println("An error occurred.");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
The csv file "matchData" contains:
UWE,KCC,Jin,Julia,Chris,Ryan,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,0,0,5,0
The csv file "scoreData" has one empty line in it
You can first go through your source CSV file and put in a map only the lines that contain unique team key....
while (matchReader.hasNextLine()) {
String data = matchReader.nextLine();
String[] record = data.split(",", 2);
Map<String, String> matchList = new TreeMap<>();
matchList.putIfAbsent(record[0], record[1]); // only unique keys are entered.
}
// TODO write to Excel each entry in the map (you don't need to check for unique keys)
Notice that writing to Excel is done after the map is complete. This is the best approach; or at least better than what you showed in your original post. With this approach, you are letting the data structure simplify your process (and no nested loops).
UPDATE:
I forgot to mention that matchList.putIfAbsent(K, V) works with Java 8 and later. If using Java 7 or older (should upgrade Java ASAP), then you will have to do the following:
String value = matchList.get(record[0]);
if (value == null) {
matchList.put(record[0], record[1]);
}
This is because Map#get(K) returns null is no entry is found OR the map allowed for null values to be entered for a given key. Otherwise, it will return the previous value. The new method introduced in Java 8 does this check automatically.

Calculating Average from a list of students taken from a .txt file

I have a separated .txt file in which there is a list of "students" with their own mark on the side that goes from 0 to 10, here is an example on how the .txt looks like:
Mark 2
Elen 3
Luke 7
Elen 9
Jhon 5
Mark 4
Elen 10
Luke 1
Jhon 1
Jhon 7
Elen 5
Mark 3
Mark 7
What I want to do is calculating the Average of each student (expressed in double) so that the output would look like this:
Mark: 4.0
Elen: 6.75
Luke: 4.0
Jhon: 4.33
Here is what I've come up with, for now I've only managed to use Properties to list the student names without repetition, but the number shown on the side of each one of them is obviously the last one the program finds.
I've included it on a button actionlistener as I'm implementing a GUI, by pressing the button the output shown above is append in a TextArea:
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent d) {
try {
File file = new File("RegistroVoti.txt");
FileInputStream fileInput = new FileInputStream(file);
Properties properties = new Properties();
properties.load(fileInput);
fileInput.close();
Enumeration enuKeys = properties.keys();
while (enuKeys.hasMoreElements()) {
String key = (String) enuKeys.nextElement();
String value = properties.getProperty(key);
l1.append(key + ": " + value + "\n");
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
I was thinking to use Collectors to calculate the average, but I actually don't know how to implement it...
Any help is appreciated!
Thanks in advance!
The way I like to do this kind of thing is using Maps and Lists.
To read the lines from the file, I'm fond of the nio way of reading, so I would do
List<String> lines = Files.readAllLines(Paths.get("RegistroVoti.txt"));
Then, you can make a HashMap<String,List<Integer>>, which will store each person's name and a list of the numbers associated with them:
HashMap<String, List<Integer>> studentMarks = new HashMap<>();
Then, using a for each loop, go through each of the lines and add each number to the hash map:
for (String line : lines) {
String[] parts = line.split(" ");
if (studentMarks.get(parts[0]) == null) {
studentMarks.put(parts[0], new ArrayList<>());
}
studentMarks.get(parts[0]).add(Integer.parseInt(parts[1]));
}
Then, you can go through each entry in the map and calculate the average of the associated list:
for (String name : studentMarks.keySet()) {
System.out.println(name + " " + studentMarks.get(name).stream().mapToInt(i -> i).average().getAsDouble());
}
(Note that this is a Java 8 stream solution; you could just as easily write a for loop to calculate it in earlier versions)
For more information on some of the things I've used, see:
String#split()
HashMap
ArrayList
Stream<T>#mapToInt()
Hope this helps!
Edit A complete solution:
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent d) {
try {
List<String> lines = Files.readAllLines(Paths.get("RegistroVoti.txt"));
Map<String, List<Integer>> studentMarks = new HashMap<>();
for (String line : lines) {
String[] parts = line.split(" ");
if (studentMarks.get(parts[0]) == null) {
studentMarks.put(parts[0], new ArrayList<>());
}
studentMarks.get(parts[0]).add(Integer.parseInt(parts[1]));
}
for (String name : studentMarks.keySet()) {
System.out.println(name + " " + studentMarks.get(name).stream().mapToInt(i -> i).average().getAsDouble());
}
} catch (IOException e) {
e.printStackTrace();
}
}
});

Outputting a Java map and mixing in HTML

I am currently working on my own version of a glossary written in Java. Truthfully, this is of academic nature and I was hoping someone could point me in the first direction. Anyway, I am reading in text from a text file and putting the words and their corresponding definitions into a Map (Tree Map to be more specific). Everything works good from there. Everything is in the map as it should be.
Now I start to get to the part where I want to go into HTML and output the contents of the map. I know how to do that with iterators and that wasn't much of a problem. However, when I try to display the content mixed in with HTML I don't get all that I want. The page is ultimately supposed to look like this: http://cse.osu.edu/~weide/rsrg/sce/now/321/421/labs/lab10/glossary.html#book
And there is this particularly tricky part where if there's a term contained within a definition it should be clickable. Here is what I have so far. Again, if anyone could help me figure out why the main guts of the HTML aren't displaying I would appreciate it very much! By the way, the text file I'm getting things from is called: terms.txt, and the html file writing to is called glossary.html.
This is what I have so far:
public class Glossary {
/**
* #param args
* #throws IOException
*/
public static void main(String[] args) throws IOException {
Map<String, String> dictionary = new TreeMap<String, String>();
File htmlFile = new File(
"/Users/myname/Documents/workspace/Lab10/src/glossary.html");
File file = new File(
"/Users/myname/Documents/workspace/Lab10/src/terms.txt");
Writer out = new OutputStreamWriter(new FileOutputStream(htmlFile));
String term = null;
String def = null;
String key = null, value = null;
String lead = null;
String multiFinalDef = null;
Set<String> checkValues = new HashSet<String>();
String leftOver = null;
boolean check = false;
Scanner input = null;
try {
input = new Scanner(file);
while (input.hasNext()) {
String keepTrack;
boolean multi = false;
String line = input.nextLine();
term = line;
def = input.nextLine();
keepTrack = def;
while (def.length() > 0 && input.hasNext()) {
def = input.nextLine();
if (def.length() > 0) {
multiFinalDef = " " + keepTrack + def;
multi = true;
}
}
if (multi) {
dictionary.put(term, multiFinalDef);
} else {
dictionary.put(term, keepTrack);
}
checkValues.add(term);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
out.write("<HTML>\n");
out.write("<HEAD>\n");
out.write("</HEAD>\n");
out.write("<BODY>\n");
out.write("<H1>Glossary</H1>\n");
out.write("<HR /\n");
out.write("<H2>Index</H2>\n");
out.write("<UL>\n");
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Set s = dictionary.entrySet();
Iterator iterator = s.iterator();
while (iterator.hasNext()) {
Map.Entry m = (Map.Entry) iterator.next();
// getKey is used to get key of map.
key = (String) m.getKey();
// getValue is used to get the value of the key in map.
value = (String) m.getValue();
// this is just so I know the output from the map is actually correct. And indeed it is.
System.out.println("Key:\t\t\tValue\n " + key + "\t\t\t " + value
+ "\n");
try {
out.write("<LI>" + key + "</LI>\n");
out.write("</UL>\n");
out.write("<HR />\n");
} catch (IOException e) {
e.printStackTrace();
}
}
out.write("<H2>Terms and Definitions</H2>\n");
out.write("<UL>\n" + "<P>\n");
iterator = s.iterator();
while (iterator.hasNext()) {
Map.Entry temp = (Map.Entry) iterator.next();
// getKey is used to get key of map.
String keyTwo = (String) temp.getKey();
// getValue is used to get the value of the key in map.
String valueTwo = (String) temp.getValue();
out.write("<H3><A NAME=\" " + keyTwo + "/><B><I><FONT COLOR=\"red\">"
+ keyTwo + "</FONT></I></B></LI></H3>\n");
for(String getTerm : checkValues){
if (valueTwo.contains(getTerm)) {
check = true;
int foundTermPosition = valueTwo.indexOf(getTerm);
lead = valueTwo.substring(0, foundTermPosition - 1);
//fix left over..
leftOver = valueTwo.substring(foundTermPosition, valueTwo.length());
out.write(lead);
out.write("" + keyTwo + "");
out.write(leftOver + "\n");
//out.write("</blockquote>\n");
}
}
if( check == false)
{
out.write(lead + " " + valueTwo);
}
}
//System.out.println(valueTwo + leftOver);
// used to put words defined in file mentioned in definition
// with hyperlinks to their associated locations, and output the
// definition.
out.write("</P>\n" + "</UL>\n");
out.write("</BODY>\n");
out.write("</HTML>");
out.close();
}
}
By the time your program reaches
out.write("<H2>Terms and Definitions</H2>\n");
out.write("<UL>\n" + "<P>\n");
while (iterator.hasNext()) {
...
the iterator doesn't have any more items left, as it gets exhausted on the first while loop a few lines before, while you're printing the index. To iterate through the map again, you'll need to call the iterator method again. So the block above would become:
out.write("<H2>Terms and Definitions</H2>\n");
out.write("<UL>\n" + "<P>\n");
iterator = s.iterator();
while (iterator.hasNext()) {
...
As I understand, you want to generate html documents. In my humble opinion, the best and generic approach in your case - use any of template engines. For example - Apache Velocity.
It takes a few minutes to look through this tutorial

How to speedup multiple searches on an HashTable

I have two files (with almost 5000 lines each) with logs. The files in each line has a set of rules associated too an email, like this:
Y#12#EMAIL_1#RULE_1,RULE_2,RULE_3,RULE_4#time=993470174
Y#12#EMAIL_2#RULE_1,RULE_2,RULE_3,RULE_4#time=993470175
Y#12#EMAIL_3#RULE_1,RULE_2,RULE_3#time=9934701778
I use the following function to read the file, and get the rules for each email:
private void processFile()
{
ArrayList<String[]> lSplitRules = new ArrayList<>();
try {
FileInputStream fileStream = new FileInputStream("log.log");
DataInputStream fileIn = new DataInputStream(fileStream);
BufferedReader fileBr = new BufferedReader(new InputStreamReader(fileIn));
String strLine;
while ((strLine = fileBr.readLine()) != null)
{
String[] lTokens = strLineSpam.split("#");
String lRawRules = lTokens[3];
lSplitRules.add(lRawRules.split(","));
}
} catch (FileNotFoundException e) {
System.out.println("File: log.log, not found. Error: " + e.getMessage());
} catch (IOException e) {
System.out.println("Couldn't open log.log. Error: " + e.getMessage());
}
So far so, good. In each "space" of the ArrayList I'll have an String[] containing the rules for each email. In other hand i have also an HashMap containing one unique list of rules and it's value like this:
RULE_NAME - VALUE
RULE_1 - 0.1
RULE_2 - 0.5
RULE_3 - 0.6
...
I need to compare every rule of every email too see if it exists on the HashMap. If exist returns the value of the rule for some calculations
I use this function for that:
private Double eval (String rule, Map<String, Double> scores)
{
for (Entry<String, Double> entry : scores.entrySet()) {
if (entry.getKey().equalsIgnoreCase(rule))
{
return entry.getValue();
}
}
return 0.0;
}
The problem is that i need to compare every email and it's rules multiple times (more then 10.000), since I'm using a Genetic Algorithm to try to optimize the VALUE of each RULE. Is there anyway to optimize the comparison of the rules of each email through the HASHMAP? Since i need speed, I'm doing 100 verifications in 8 minutes now.
Sorry for my english.
Regards
The whole point of having a hash table is so youc an do a single hash lookup. If you are just going to loop through the keys, you may as well use a List.
I don't know where you are building your scores, but you can normalise the case.
scores.put(key.toLowerCase(), value);
for a case insensive lookup
Double d= scores.get(key.toLowerCase());

Categories

Resources