This question already has answers here:
How to append text to an existing file in Java?
(31 answers)
Closed 6 years ago.
This code will iterate through a number of pages to find and extract elements on the page. Once the loop has completed it will generate a log file with these elements from a HashMap, but the results aren't being appended and are rather being overwritten.
int d = new Integer(0);
for (int i = 0; i <= 100; d += 10) {
String url = Constants.FilterUrl + "&startIndex=" + d;
this.getAuthors();
driver.get(url);
if (!driver.getPageSource().contains("h3")) break;
}
/* Send HashMap values to text file */
File file = new File(Constants.FILEPATH + Constants.dateFormat.format(new Date()) + ".txt");
try{
if(!file.exists()){
System.out.println("We had to make a new file.");
file.createNewFile();
}
PrintWriter out = new PrintWriter(new FileWriter(file), true);
map.forEach((k, v) -> out.println(k + ", " + v));
out.append("************** " + "\n");
out.close();
} catch(IOException e) {
System.out.println("COULD NOT LOG!!");
}
}
public void getAuthors(){
List<WebElement> allElements = driver.findElements(By.tagName("h3"));
/* Create HashMap and store H3 elements in the key set */
this.map = new HashMap<String, String>();
for (WebElement element1 : allElements) {
map.put(element1.getText(), element1.findElement(By.tagName("a")).getAttribute("href"));
}
/* Visit pages for H3 elements and retrieve names of the authors */
for (Map.Entry<String, String> entry : map.entrySet()) {
driver.get(entry.getValue());
entry.setValue(driver.findElement(By.className("userlink-0")).getText());
}
}
Any ideas?
map.put(element1.getText(),
element1.findElement(By.tagName("a")).getAttribute("href"));
If there is any entry in HashMap with the same text as element1.getText(), it will override it.
Also you are creating map for each call, it will create a new Map each time and lead to data loss for earlier content.
/* Create HashMap and store H3 elements in the key set */
this.map = new HashMap<String, String>();
You should create this at instance level.
For generating unique key, define a number variable at instance level and increment that for each put.
long counter = 0;
map.put(counter++, element1.findElement(By.tagName("a")).getAttribute("href"));
May be change the HashMap to take long as Key instead of String.
for (WebElement element1 : allElements) {
i++
map.put(element1.getText()+i, element1.findElement(By.tagName("a")).getAttribute("href"));
}
add i++ so it doesnt override
Related
Be gentle,
This is my first time using Apache Commons CSV 1.7.
I am creating a service to process some CSV inputs,
add some additional information from exterior sources,
then write out this CSV for ingestion into another system.
I store the information that I have gathered into a list of
HashMap<String, String> for each row of the final output csv.
The Hashmap contains the <ColumnName, Value for column>.
I have issues using the CSVPrinter to correctly assign the values of the HashMaps into the rows.
I can concatenate the values into a string with commas between the variables;
however,
this just inserts the whole string into the first column.
I cannot define or hardcode the headers since they are obtained from a config file and may change depending on which project uses the service.
Here is some of my code:
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get(OUTPUT + "/" + project + "/" + project + ".csv"));)
{
CSVPrinter csvPrinter = new CSVPrinter(writer,
CSVFormat.RFC4180.withFirstRecordAsHeader());
csvPrinter.printRecord(columnList);
for (HashMap<String, String> row : rowCollection)
{
//Need to map __record__ to column -> row.key, value -> row.value for whole map.
csvPrinter.printrecord(__record__);
}
csvPrinter.flush();
}
Thanks for your assistance.
You actually have multiple concerns with your technique;
How do you maintain column order?
How do you print the column names?
How do you print the column values?
Here are my suggestions.
Maintain column order.
Do not use HashMap,
because it is unordered.
Instead,
use LinkedHashMap which has a "predictable iteration order"
(i.e. maintains order).
Print column names.
Every row in your list contains the column names in the form of key values,
but you only print the column names as the first row of output.
The solution is to print the column names before you loop through the rows.
Get them from the first element of the list.
Print column values.
The "billal GHILAS" answer demonstrates a way to print the values of each row.
Here is some code:
try (BufferedWriter writer = Files.newBufferedWriter(
Paths.get(OUTPUT + "/" + project + "/" + project + ".csv"));)
{
CSVPrinter csvPrinter = new CSVPrinter(writer,
CSVFormat.RFC4180.withFirstRecordAsHeader());
// This assumes that the rowCollection will never be empty.
// An anonymous scope block just to limit the scope of the variable names.
{
HashMap<String, String> firstRow = rowCollection.get(0);
int valueIndex = 0;
String[] valueArray = new String[firstRow.size()];
for (String currentValue : firstRow.keySet())
{
valueArray[valueIndex++] = currentValue;
}
csvPrinter.printrecord(valueArray);
}
for (HashMap<String, String> row : rowCollection)
{
int valueIndex = 0;
String[] valueArray = new String[row.size()];
for (String currentValue : row.values())
{
valueArray[valueIndex++] = currentValue;
}
csvPrinter.printrecord(valueArray);
}
csvPrinter.flush();
}
for (HashMap<String,String> row : rowCollection) {
Object[] record = new Object[row.size()];
for (int i = 0; i < columnList.size(); i++) {
record[i] = row.get(columnList.get(i));
}
csvPrinter.printRecord(record);
}
I have a big CSV file, thousands of rows, and I want to aggregate some columns using java code.
The file in the form:
1,2012,T1
2,2015,T2
3,2013,T1
4,2012,T1
The results should be:
T, Year, Count
T1,2012, 2
T1,2013, 1
T2,2015, 1
Put your data to a Map like structure, each time add +1 to a stored value when a key (in your case ""+T+year) found.
You can use map like
Map<String, Integer> rowMap = new HashMap<>();
rowMap("T1", 1);
rowMap("T2", 2);
rowMap("2012", 1);
or you can define your own class with T and Year field by overriding hashcode and equals method. Then you can use
Map<YourClass, Integer> map= new HashMap<>();
T1,2012, 2
String csv =
"1,2012,T1\n"
+ "2,2015,T2\n"
+ "3,2013,T1\n"
+ "4,2012,T1\n";
Map<String, Integer> map = new TreeMap<>();
BufferedReader reader = new BufferedReader(new StringReader(csv));
String line;
while ((line = reader.readLine()) != null) {
String[] fields = line.split(",");
String key = fields[2] + "," + fields[1];
Integer value = map.get(key);
if (value == null)
value = 0;
map.put(key, value + 1);
}
System.out.println(map);
// -> {T1,2012=2, T1,2013=1, T2,2015=1}
Use uniVocity-parsers for the best performance. It should take 1 second to process 1 million rows.
CsvParserSettings settings = new CsvParserSettings();
settings.selectIndexes(1, 2); //select the columns we are going to read
final Map<List<String>, Integer> results = new LinkedHashMap<List<String>, Integer>(); //stores the results here
//Use a custom implementation of RowProcessor
settings.setRowProcessor(new AbstractRowProcessor() {
#Override
public void rowProcessed(String[] row, ParsingContext context) {
List<String> key = Arrays.asList(row); // converts the input array to a List - lists implement hashCode and equals based on their values so they can be used as keys on your map.
Integer count = results.get(key);
if (count == null) {
count = 0;
}
results.put(key, count + 1);
}
});
//creates a parser with the above configuration and RowProcessor
CsvParser parser = new CsvParser(settings);
String input = "1,2012,T1"
+ "\n2,2015,T2"
+ "\n3,2013,T1"
+ "\n4,2012,T1";
//the parse() method will parse and submit all rows to your RowProcessor - use a FileReader to read a file instead the String I'm using as example.
parser.parse(new StringReader(input));
//Here are the results:
for(Entry<List<String>, Integer> entry : results.entrySet()){
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
Output:
[2012, T1] -> 2
[2015, T2] -> 1
[2013, T1] -> 1
Disclosure: I am the author of this library. It's open-source and free (Apache V2.0 license).
Right now, I'm trying to get this method of mine to print a .txt file of a HashMap that contains a word as a Key, and the number of times it appears in a read .txt file (done in another method) as a Value. The method needs to put the HashMap Keys in alphabetical order, and then print the corresponding Value next to it in a separate .txt file.
Here is my code for the method:
public static void writeVocabulary(HashMap<String, Integer> vocab, String fileName) {
// Converts the given HashMap keys (the words) into a List.
// Collections.sort() will sort the List of HashMap keys into alphabetical order.
List<String> listVal = new ArrayList<String>(vocab.keySet());
Collections.sort(listVal);
try
{
// Creating the writer
PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(fileName)));
for (int i = 1; i < listVal.size(); i++) {
out.println(listVal.get(i) + " " + vocab.get(i));
}
out.close();
}
// Catching the file not found error
// and any other errors
catch (FileNotFoundException e) {
System.err.println(fileName + "cannot be found.");
}
catch (Exception e) {
System.err.println(e);
}
}
My problem is that, while a .txt file is printed, and the words are in perfect ASCII order (what I need), every value next to the word is returned null. I've tried many different ways of fixing this but to no avail. I think the problem is in my 'for' loop:
for (int i = 1; i < listVal.size(); i++) {
out.println(listVal.get(i) + " " + vocab.get(i));
}
I'm pretty sure my logic in this is faulty but I can't think of a solution. Any help would be much appreciated. Thanks in advance!
You need to use the correct map key to get the value from the map - this code currently uses the index in the List, not the value in the list (which is the actual key to the map).
for (int i = 0; i < listVal.size(); i++) {
out.println(listVal.get(i) + " " + vocab.get(listVal.get(i)));
}
And also start at index 0 if you want all the items (see initial condition in loop above). As suggested in a comment, you can alternatively use a TreeMap to iterate over the keys of the map in order
This is where an enhanced for loop would have kept you from making a mistake. You get values from a Map by using get(key):
for ( String key : listVal ) {
out.println( key + " " + vocab.get(key) );
}
You don't need to iterate through the list using the indexes; instead you can use:
for ( final String key : listVal ) {
out.println( key + " " + vocab.get( key ) );
}
and you can simplify things even further using a TreeSet to do the sorting:
for ( final String key : new TreeSet<String>( vocab.keySet() ) ) {
out.println( key + " " + vocab.get( key ) );
}
I'm trying to put some Strings to a HashMap, but they wont add.
My code looks like this and I can't seem to understand why they won't add. Can someone help me and give me an explanation to what I'm doing wrong?
HashMap <String, String> akro = new HashMap <String, String>();
public void lesFil() {
try {
BufferedReader br = new BufferedReader(new FileReader("akronymer.txt"));
String line;
while((line = br.readLine()) != null) {
if(!line.contains(" ")) {
continue;
}
String[] linje = line.split("\\s+", 2);
String akronym = linje[0];
String betydning = linje[1];
// System.out.println(a + " || " + b);
akro.put(akronym, betydning);
}
} catch(Exception e) {
System.out.println("Feilen som ble fanget opp: " + e);
}
}
When I'm removing "//", both akronym and betydning prints out fine.
I tried to add this method to test the HashMap but nothing prints out and the size = 0
public void skrivUt() {
for(Map.Entry <String, String> entry : akro.entrySet()) {
System.out.print("Key: " + entry.getKey());
System.out.println(", Value: " + entry.getValue());
}
System.out.println("Antall akronymer: " + akro.size());
}
Part of the file I'm reading from(txt file):
...
CS Chip Select
CS Clear to Send
CS Code Segment
C/S Client/Server
...
Remember that a Map in Java maps one key to one value. In the sample data you provide, it seems that you have multiple values ("Chip Select", "Clear to Send", "Code Segment") for one key ("CS").
You can solve this by either picking a structure other than a Map, changing what you want to store, or changing the value of the Map to a List<String>.
For example:
List<String> values = akro.get(akronym);
if(values == null) {
values = new LinkedList<String>();
akro.put(akronym, values);
}
values.add(betydning);
I have a hashmap and I am inserting an arraylist as the value in a while loop. During an iteration, if the hashmap already contains a key, I want to retreive the arraylist already stored, append new data to it and put it back into the hashmap.
The issue I have now is when I check if a hashmap contains a value and it does, the arraylist being returned is of size 0 as though I never put in anything before.
final HashMap<String, ArrayList<String>> map = new HashMap<String, ArrayList<String>>();
String status = "";
String channel = "";
while (daily.next()) {
while (avrg.next())
if (avrg.getString(1).equals(daily.getString(1)) && avrg.getString(2).equals(daily.getString(2))) {
final Object pstmnt = null;
final Object pstmnt2 = null;
final float thresholdValue = calcThreshold(pstmnt, pstmnt2, daily.getString(1));
channel = daily.getString("client_name");
if (daily.getFloat(3) > avrg.getFloat(3) + thresholdValue * avrg.getFloat(3)) {
status = "HIGHER";
}
else if (daily.getFloat(3) < avrg.getFloat(3) - thresholdValue * avrg.getFloat(3)) {
status = "LOWER";
}
else {
status = "Normal";
}
final PrintStream out;
out.println(channel);
out.println(thresholdValue);
out.println(status);
if (map.containsKey(channel)) {
out.println("map contained key");
final ArrayList<String> temp = new ArrayList<String>();
temp.addAll(map.get(channel));
out.println("previous size: " + temp.size());
temp.add(daily.getString("event"));
temp.add(Float.toString(daily.getFloat(3)));
temp.add(Float.toString(avrg.getFloat(3)));
temp.add(Float.toString(thresholdValue * 100));
temp.add(status);
out.println("current size: " + temp.size());
map.put(channel, temp);
out.println("array added to map");
temp.clear();
System.out.println("done");
}
else {
final ArrayList<String> temp = new ArrayList<String>();
out.println("new key created");
temp.add(daily.getString("event"));
temp.add(Float.toString(daily.getFloat(3)));
temp.add(Float.toString(avrg.getFloat(3)));
temp.add(Float.toString(thresholdValue * 100));
temp.add(status);
System.out.println(temp.size());
map.put(channel, temp);
System.out.println("array added to map");
}
}
avrg.beforeFirst();
}
I am really not sure why this is happening. Any help would be appreciated!
The problem is right here:
map.put(channel, temp);
out.println("array added to map");
temp.clear();
temp.clear() clears the same list that you've just added to the map. Since temp is about to go out of scope, that statement serves no useful purpose and can be removed.
Let me start by saying that #aix has the correct answer, but you should rewrite you code to get rid of all the duplication. The if block starting from
if (map.containsKey(channel))
can be simplified to the following.
ArrayList<String> temp = map.get(channel);
if (temp == null) {
out.println("new key created");
ArrayList<String> temp = new ArrayList<String>();
map.put(channel, temp);
}
temp.add(daily.getString("event"));
temp.add(Float.toString(daily.getFloat(3)));
temp.add(Float.toString(avrg.getFloat(3)));
temp.add(Float.toString(thresholdValue * 100));
temp.add(status);
There is no need to create a new list and copy every time you add to it.