String Array into arraylist? - java

I have a csv file with unknown amount of columns and row. The only thing I know is that each entry is separated by a comma. Can I use the split method to convert each line of the data into an array and then can I store that Array into an Arraylist. One of the things that concerns me is would I be able to rearrange the Arraylist alphabetically or numerically.

I would suggest using OpenCSV. If you just split on the comma separator, and you happen to have a single cell text containing a comma, but which is enclosed in double quotes to make it clear that it's a single cell, the split method won't work:
1, "I'm a single cell, with a comma", 2
3, hello, 4
OpenCSV will let you read each line as an array of Strings, handling this problem for you, and you can of course store each array inside a List. You will need a custom comparator to sort your list of lines. Search StackOverflow: the question of how to sort a list comes back twice a day here.

Yes, you can use:
String[] array = input.split("\",\"");
List<String> words = new ArrayList<String>(Arrays.asList(array))
Note that Arrays.asList(..) also returns a List, but you can't modify it.
Also note that the above split is on ",", because CVSs usually look like this:
"foo","foo, bar"

Using split with simple comma is not a fool proof one. If your column data contains a comma, csv would be stored something like a,"b,x",c. In such case split would fail.
I'm not a regex expert maybe someone could write a EMBEDDED_COMMA_DETECTING_REGEX or GIYF.
String[] array = input.split(EMBEDDED_COMMA_DETECTING_REGEX);
List<String> words = new ArrayList<String>(Arrays.asList(array));

There are several questions here so I'll cover each point individually.
Can I use the split method convert each line of the data into an array
This would work as you expect in the naive case. However, it doesn't know anything about escaping; so if a comma is embedded within a field (and properly escaped, usually by double-quoting the whole field) the simple split won't do the job here and will chop the field in two.
If you know you'll never have to deal with embedded commas, then calling line.split(",") is acceptable. The real solution however is to write a slightly more involved parse method which keeps track of quotes, and possibly backslash escapes etc.
...into an array than can I store that Array into an Arraylist
You certainly could have an ArrayList<String[]>, but that doesn't strike me as particularly useful. A better approach would be to write a simple class for whatever it is the CSV lines are representing, and then create instances of that class when you're parsing each line. Something like this perhaps:
public class Order {
private final int orderId;
private final String productName;
private final int quantity;
private final BigDecimal price;
// Plus constructor, getters etc.
}
private Order parseCsvLine(String line) {
String[] fields = line.split(",");
// TODO validation of input/error checking
final int orderId = Integer.parseInt(fields[0]);
final String productName = fields[1];
final int quantity = Integer.parseInt(fields[2]);
final BigDecimal price = new BigDecimal(fields[3]);
return new Order(orderId, productName, quantity, price);
}
Then you'd have a list of Orders, which more accurately represents what you have in the file (and in memory) than a list of string-arrays.
One of the things that concerns me is would I be able to rearrange the Arraylist according alphabetically or numerically?
Sure - the standard collections support a sort method, into which you can pass an instance of Comparator. This takes two instances of the object in the list, and decides which one comes before the other.
So following on from the above example, if you have a List<Order> you can pass in whatever comparator you want to sort it, for example:
final Comparator<Order> quantityAsc = new Comparator<Order>() {
public int compare(Order o1, Order o2) {
return o2.quantity - o1.quantity; // smaller order comes before bigger one
}
}
final Comparator<Order> productDesc = new Comparator<Order>() {
public int compare(Order o1, Order o2) {
if (o2.productName == null) {
return o1.productName == null ? 0 : -1;
}
return o2.productName.compareTo(o1.productName);
}
}
final List<Order> orders = ...; // populated by parsing the CSV
final List<Order> ordersByQuantity = Collections.sort(orders, quantityAsc);
final List<Order> ordersByProductZToA = Collections.sort(orders, productDesc);

Related

Java Stream - Retrieving repeated records from CSV

I searched the site and didn't find something similar. I'm newbie to using the Java stream, but I understand that it's a replacement for a loop command. However, I would like to know if there is a way to filter a CSV file using stream, as shown below, where only the repeated records are included in the result and grouped by the Center field.
Initial CSV file
Final result
In addition, the same pair cannot appear in the final result inversely, as shown in the table below:
This shouldn't happen
Is there a way to do it using stream and grouping at the same time, since theoretically, two loops would be needed to perform the task?
Thanks in advance.
You can do it in one pass as a stream with O(n) efficiency:
class PersonKey {
// have a field for every column that is used to detect duplicates
String center, name, mother, birthdate;
public PersonKey(String line) {
// implement String constructor
}
// implement equals and hashCode using all fields
}
List<String> lines; // the input
Set<PersonKey> seen = new HashSet<>();
List<String> unique = lines.stream()
.filter(p -> !seen.add(new PersonKey(p))
.distinct()
.collect(toList());
The trick here is that a HashSet has constant time operations and its add() method returns false if the value being added is already in the set, true otherwise.
What I understood from your examples is you consider an entry as duplicate if all the attributes have same value except the ID. You can use anymatch for this:
list.stream().filter(x ->
list.stream().anyMatch(y -> isDuplicate(x, y))).collect(Collectors.toList())
So what does the isDuplicate(x,y) do?
This returns a boolean. You can check whether all the entries have same value except the id in this method:
private boolean isDuplicate(CsvEntry x, CsvEntry y) {
return !x.getId().equals(y.getId())
&& x.getName().equals(y.getName())
&& x.getMother().equals(y.getMother())
&& x.getBirth().equals(y.getBirth());
}
I've assumed you've taken all the entries as String. Change the checks according to the type. This will give you the duplicate entries with their corresponding ID

How to add elements from several arrays to a new static array without ArrayList?

For a school assignment, I have successfully created a program which stores recipes (name, ingredients, instructions). I now need to add a shopping list feature which will take the ingredients from the recipes I've added and store them in a new static string array.
I have been required to create the ingredients string array with a size of 20 and the shoppingList as a static string array with a size of 40:
private String[] ingredients = new String[20];
private static String[] shoppingList = new String[40];
I initialized all elements of both arrays to an empty string.
I've also hard coded two recipes in my test program like this:
rcp1.setIngredients(0, "Butter");
rcp1.setIngredients(1, "Garlic");
rcp1.setIngredients(2, "Parsley");
rcp1.setIngredients(3, "Bread");
rcp1.setIngredients(4, "Cheese");
rcp2.setIngredients(0, "Biscuits");
rcp2.setIngredients(1, "Pepperoni");
rcp2.setIngredients(2, "Pizza sauce");
This is where I get stumped:
public void setIngredients(int loc, String ingredient) {
ingredients[loc] = ingredient;
for (int i=0; i<shoppingList.length; i++){
if (shoppingList[loc] == "") {
shoppingList = ingredients;
}
}
}
Because the ingredients of rcp1 and rcp2 both use the ingredients locations [0, 1, 2], trying to add the ingredient to the static shoppingList using [loc] results in it overwriting those locations of the first recipe.
I've created the for loop so it could properly display the first recipe's ingredients; however, I'm not sure how to then proceed to find the location of the next empty string in order to list the ingredients of the second recipe. I was thinking that I would have to use an else if statement and then figure out a way to find where the next empty string is so I can begin to add the next recipe's ingredients from that point..?
Is there any other, possibly simpler, way that I could take the ingredients from rcp2 and place them after rcp1's ingredients in shoppingList?
I think I've been over-analyzing this problem and I haven't been able to find a solution yet, so I'd greatly appreciate it if anyone could point me in the right direction. Thank you!
You are running into a lot of artificial constraints, which is expected since this is a course assignment. Your main problem is that - apparently - you don't know how many ingredients are in each recipe, or in the shopping list. Normally, the array would be the right size for the contents so you would have the number of items in arr.length. Otherwise one would use one of the classes in the collections framework, like ArrayList.
However, you are using fixed-size buffers, which means that you need to track the current size of your list in addition to the capacity, which is what arr.length gives you. There are two ways you can do it:
Explicitly, by having an int variable that is conceptually associated with the array to mean "first unused element". When you add new elements, you would start at that index, and increment that variable for every new ingredient added to the list. This is what ArrayList does.
Implicitly, by having some kind of "marker" String that would mean "unused". This can be a proper String instance, or simply null. This is basically what C does with the '\0' string terminator.
Each option has different trade-offs and requires a different implementation, which I'm not adding here since you need do think about it as part of your course! Do not hesitate to ask more if you're stuck again, though.

Order values by separate string

Sorry about the non-descriptive title, I couldn't think of a way to explain it better short of 100 words. What I would like to be able to do is sort a list of strings into "boxes" based on a string associated with the main string and an array of strings "order".
For my current setup I am using a HashMap to store the string and it's associated "place-in-the-order" string.
I am aware that my explanation is truly crap so I have made an image which I hope will explain it better:
The variables are initialised as follows:
private final String[] order = new String[] {"Severe", "Warning", "Info"};
private final Box[] boxes = new Box[] {new Box(1), new Box(2), new Box(3), new Box(4)};
private final Map<String, String> texts = new HashMap<String, String>();
texts.put("Server on fire!", "Severe");
texts.put("All is good!", "Info");
texts.put("Monkeys detected!", "Warning");
texts.put("Nuke activated!", "Severe");
This shouldn't be too hard to implement but the only way I can think of doing it is by using 3 loops which seems a bit wasteful and would be slow if there was large numbers of any of the inputs.
Here is some example code which will hopefully show what I have come up with so far and perhaps explain the problem, I have not tested it and don't have an IDE handy so have probably overlooked something.
Set<Box> usedBoxes = new HashSet<Box>();
for(String curOrder : order) {
for (String text : texts) {
if (texts.get(text).equals(order)) {
for (Box box : boxes) {
if (!usedBoxes.contains(box)) {
box.setText(text);
usedBoxes.add(box);
break;
}
}
}
}
}
I'm not sure I fully understand what you want to achieve, but I feel that there are two things that would make your design much simpler:
Don't use Strings for your severity levels. Use enums instead. Enums have a name, may have other fields and methods, and are naturally ordered using their order of definition. And there is no way to make a typo and introduce an unknown severity: they're type-safe
enum Severity {
SEVERE, WARNING, INFO
}
Don't store things in parallel arrays or associate them with maps. Define a class containing the information of your objects:
public class Box {
private String text;
private Severity severity;
}
Now that you have these, you can simply create a List<Box>, and sort it using a Comparator<Box> which sorts them by severity, for example:
List<Box> boxes = Arrays.asList(new Box("Server is on fire", Severity.SEVERE),
new Box("All is good", Severity.INFO),
...);
Collections.sort(boxes, new Comparator<Box>() {
#Override
public int compare(Box b1, Box b2) {
return b1.getSeverity().compareTo(b2.getSeverity());
}
}
or even simpler, with Java 8:
boxes.sort(Comparator.comparing(Box::getSeverity));
You should make your "statuses" (Severe, Info etc) into an Enum.
public enum StatusLevel {
Severe,
Warning,
Info;
}
You can then sort by StatusLevel natively as long as you define the in a top to bottom order.
If you want to supply your Box object directly insead of pulling out the StatusLevel or have a secondary sort by another property like time or alphabetically you should implement your own Comparator
Also you may want to look into SortedMap or other Map that keeps its order so you don't have to resort a HashMap every time as it does not guarantee order.

Best way to save some data and then retrieve it

I have a project where I save some data coming from different channels of a Soap Service, for example:
String_Value Long_timestamp Double_value String_value String_value Int_value
I can have many lines (i.e. 200), with different values, like the one above.
I thought that I could use an ArrayList, however data can have a different structure than the one above, so an ArrayList maybe isn't a good solution in order to retrieve data from it.
For example above I have, after the first two values that are always fixed, 4 values, but in another channel I may have 3, or 5, values. What I want retrieve data, I must know how many values have a particular line, and I think that Arraylist doesn't help me.
What solution could I use?
When you have a need to uniquely identify varying length input, a HashMap usually works quite well. For example, you can have a class:
public class Record
{
private HashMap<String, String> values;
public Record()
{
// create your hashmap.
values = new HashMap<String, String>();
}
public String getData(String key)
{
return values.get(key);
}
public void addData(String key, String value)
{
values.put(key, value);
}
}
With this type of structure, you can save as many different values as you want. What I would do is loop through each value passed from Soap and simply add to the Record, then keep a list of Record objects.
Record rec = new Record();
rec.addData("timestamp", timestamp);
rec.addData("Value", value);
rec.addData("Plans for world domination", dominationPlans);
You could build your classes representing the entities and then build a parser ... If it isn't in a standard format (eg JSON, YAML, ecc...) you have no choice to develop your own parser .
Create a class with fields.
class ClassName{
int numberOfValues;
String dataString;
...
}
Now create an ArrayList of that class like ArrayList<ClassName> and for each record fill that class object with numberOfValues and dataString and add in Arraylist.

Why does my XML parser only returns one string, instead of multiple ones?

I got a problem regarding parsing XML data. I have divided my program into 3 different java files, each containing a class. One of them is rssparser.java. This file holds a function called iterateRSSFeed(String URL), this function returns a string containing the parsed description tag. In my main.java files where my main method is, I call this iterateRSSFeed function this way:
rssparser r = new rssparser();
String description = r.iterateRSSFeed();
And then I am planning to add this String to a JLabel, this way:
JLabel news = new JLabel(description);
which obviously works great, my program runs. BUT there are more description tags in my XML file, the JLabel only contains one(1) parsed description tag. I should say that my return statement in the iterateRSSFeed function is "packed" in a for-loop, which in my head should return all of the description tags. But no.
Please ask if something is uncleared or showing of the source code is a better way to provide a solution to my answer. Thanks in advance! :)
When Java executes a return statement, it will leave the method, and not continue running the loop.
If you want to return multiple values from a method, you have to put them in some object grouping them together. Normally one would use a List<String> as return type.
Then your loop will fill the list, and the return statement (after the loop) can return the whole list at once.
If you want to have one large string instead of multiple ones, you'll have to merge them into one.
The easiest would be to simply use the .toString() method on the list, this will give (if you are using the default list implementations) something like [element1, element2, element3].
If you don't like the [,], you could simply concatenate them:
List<String> list = r.iterateRSSFeed();
StringBuilder b = new StringBuilder();
for(String s : list) {
b.append(s);
}
String description = b.toString();
This will give element1element2element3.
As Java's JLabel has some rudimentary HTML support, you could also use this to format your list as a list:
List<String> list = r.iterateRSSFeed();
StringBuilder b = new StringBuilder();
b.append("<html><ul>");
for(String s : list) {
b.append("<li>");
b.append(s);
b.append("</li>");
}
b.append("</ul>");
String description = b.toString();
The result will be <html><ul><li>element1</li><li>element2</li><li>element3</li></ul>, which will be formatted by the JLabel as something like this:
element1
element2
element3

Categories