I have a String which I need to split and add to different arrays.
This is my String
{"locations":[{"latitude":"1.3846519","longitude":"103.763276","startTime":"1422720220292","duration":"0","accuracy":"50.981998443604"},{"latitude":"1.3845814","longitude":"103.7634384","startTime":"1422720520181","duration":"0","accuracy":"55.532001495361"},{"latitude":"1.3844195","longitude":"103.763209","startTime":"1422720820265","duration":"0","accuracy":"34.5"},{"latitude":"1.3844051","longitude":"103.7632272","startTime":"1422721120466","duration":"0","accuracy":"36"},
],"success":1}
The output I want is like this in different arrays.
latitudeArray[] = // String array of latitude values
longitudeArray[] = // String array of longitude values
startTimeArray[] = // String array of start time values
durationArray[] = // String array of duration values
accuracyArray[] = // String array of accuracy values
I am using processing IDE to analyse my data and I tried matchAll() and split() functions but couldn't get it work.
Could you please help me in getting my output? Thanks.
Edit: I managed to extract one latitude value but my method seems very inefficient. How can I do this inside a loop?
String[] locationData = loadStrings("sample.txt");
ArrayList<String> latitudeArray = new ArrayList<String>();
ArrayList<String> longitudeArray = new ArrayList<String>();
ArrayList<String> startTimeArray = new ArrayList<String>();
ArrayList<String> durationArray = new ArrayList<String>();
ArrayList<String> accuracyArray = new ArrayList<String>();
String temp;
int index;
index = locationData[0].indexOf("latitude");
println(index);
temp = locationData[0].substring(index+11);
println(temp);
index = temp.indexOf(",");
println(index);
latitudeArray.add(temp.substring(0,(index-1)));
println(latitudeArray.get(0));
Wasn't sure in what format the loadStrings() method returns, so I just used the initial String you provided.
You're heading in the right direction with the string methods. This code tries to benefit from the single input string. If you split on "latitude", then all the elemets in the array, except for the first one, will have the numbers we're interested on in the begining. E.g.: split("latitude\":\"") gives all the latitudes in the begining:
[0] = {"locations":[{"
[1] = 1.3846519","longitude":"103.763276","startTime":"1422720220292","duration":"0","accuracy":"50.981998443604"},{"
[2] = 1.3845814","longitude":"103.7634384","startTime":"1422720520181","duration":"0","accuracy":"55.532001495361"},{"
[3] = 1.3844195","longitude":"103.763209","startTime":"1422720820265","duration":"0","accuracy":"34.5"},{"
[4] = 1.3844051","longitude":"103.7632272","startTime":"1422721120466","duration":"0","accuracy":"36"}, ],"success":1}
To read the actual numbers, we just need to read until the next quote("). Doing indexOf("\"") will give use the position till which we must read to retrieve that number. So, just perform a substring(0,indexOfQuote) on it to get the value. The repeat again, but this time splitting on "longitude" to get them.
Full program:
public static void main(String[] args) {
final String INPUT = "{\"locations\":["
+ "{\"latitude\":\"1.3846519\",\"longitude\":\"103.763276\",\"startTime\":\"1422720220292\",\"duration\":\"0\",\"accuracy\":\"50.981998443604\"},"
+ "{\"latitude\":\"1.3845814\",\"longitude\":\"103.7634384\",\"startTime\":\"1422720520181\",\"duration\":\"0\",\"accuracy\":\"55.532001495361\"},"
+ "{\"latitude\":\"1.3844195\",\"longitude\":\"103.763209\",\"startTime\":\"1422720820265\",\"duration\":\"0\",\"accuracy\":\"34.5\"},"
+ "{\"latitude\":\"1.3844051\",\"longitude\":\"103.7632272\",\"startTime\":\"1422721120466\",\"duration\":\"0\",\"accuracy\":\"36\"},"
+ " ],\"success\":1}";
String latitudeArray[] = splitAndCollect("latitude", INPUT);
String longitudeArray[] = splitAndCollect("longitude", INPUT);
String startTimeArray[] = splitAndCollect("startTime", INPUT);
String durationArray[] = splitAndCollect("duration", INPUT);
String accuracyArray[] = splitAndCollect("accuracy", INPUT);
System.out.println("Done");
}
private static String[] splitAndCollect(String string, String input) {
final String COLON = "\":\"";
String[] split = input.split(string + COLON);
String[] output = new String[split.length - 1];
for (int i = 0; i < output.length; i++)
// Using [i+1] - since split[0] contains "locations".
// Subsequent splits will have the numbers needed.
output[i] = split[i + 1].substring(0, split[i + 1].indexOf("\""));
System.out.println(string + "\n" + Arrays.toString(output));
return output;
}
If you can preprocess the file to csv. file using simple shell script, then do string processing in java, I think you can get better performance. For csv. file processing in Java, refer http://www.mkyong.com/java/how-to-read-and-parse-csv-file-in-java/ (This blog contains simple sample).
If you do some preprocessing step (even in Java) before parsing, you can get all the values to those string arrays simply with one loop. You can use method suggested by Vineet using single loop. So with preprocessing step overall loop count becomes 2.
Thanks,
Mili
It seems that you have data in JSON format. The way you are trying to get data from the is quite difficult (but doable). You can try JSON parser . Its easy to learn and use. You can find one example here.
Related
Currently I am having a hard time trying to figure out if there is a better way to refactor the following code.
Given the following:
String detail = "POTATORANDOMFOOD";
Lets say I want to assign variables with different parts of detail, the end result would look something like this.
String title = detail.substring(0, 6); // POTATO
String label = detail.substring(6, 12); // RANDOM
String tag = detail.substring(12, 16); // FOOD
Now lets say the string detail length constantly changes, sometimes it only contains "POTATORANDOM" and no "FOOD", sometimes it contains even more characters "POTATORANDOMFOODTODAY", so another variable would be used.
String title = detail.substring(0, 6); // POTATO
String label = detail.substring(6, 12); // RANDOM
String tag = detail.substring(12, 16); // FOOD
...
String etc = detail.substring(30, 40); // etc value from detail string
The issue with this, is that since the string sometimes is shorter or longer, we would run into the StringIndexOutOfBoundsException which is not good.
So currently I have a naive way to handle this:
if (detail != null || !detail.isEmpty()) {
if (detail.length() >= 6) {
title = detail.substring(0, 6);
if (detail.length() >= 12) {
label = detail.substring(6, 12);
if (detail.length() >= 16) {
tag = detail.substring(12, 16);
.
.
.
}
}
}
}
This can get really messy, especially if lets say the string were to grow even more.
So my question is, what would be a good design pattern that would fit for this type of problem? I have tried the chain of responsibility design pattern but, the issue with this one is that it only returns a single value, while I am trying to return multiple ones if possible. This way I can assign multiple variables depending on the length of the string.
Any help/hints is greatly appreciated!
Edited:
The order and length are always the same. So title will always be first and it will always contain 6 characters. label will always be second and it will always contain 6 characters. tag will always be third and it will always contain 4 characters, etc.
If I was you, I would do the following:
Define a class to hold a Word definition
public class Word {
private final String name;
private final int startIndex;
private final int endIndex;
public Word(String name, int startIndex, int endIndex) {
this.name = name;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public String getName() { return name; }
public int getStartIndex() { return startIndex; }
public int getEndIndex() { return endIndex; }
}
Create a static list which holds all the possible words
public static final List<Word> WORDS = List.of(
new Word("title", 0, 6),
new Word("label", 6, 12),
new Word("tag", 12, 16),
...
);
Create a function that parses the String detail by walking this list until when the size of the string is exhausted
... and of course storing the elements into a Map<String, String> so that you can access them later.
public Map<String, String> parseDetail(String detail) {
Map<String, String> receivedWords = new LinkedHashMap<>(); //<-- map respecting insertion order
if (detail.isEmpty()) {
return receivedWords;
}
int parsedLength = 0;
for (Word word : WORDS) {
receivedWords.put(word.getName(), detail.substring(word.getStartIndex(), word.getEndIndex()); //<-- store the current word
parsedLength += word.getEndIndex() - word.getStartIndex(); //increase the parsedLength by the length of your word
if (parsedLength >= detail.length()) {
break; //<-- exit the loop when you're done with the parsing
}
}
return receivedWords;
}
To sum up:
Map<String, String> receivedWords = parseDetail(detail);
receivedWords.forEach((k, v) -> {
System.out.println("Key: " + k + ", value: " + v);
});
Output:
Key: title, value: POTATO
Key: label, value: RANDOM
Key: tag, value: FOOD
...
Tip 1: The input you receive looks pretty weird. I understand that you cannot change it but I would try to negotiate with the caller (if possible) a better way to send you their input (ideally a structured object, if not possible at least a string with some separator so that you can simply split by that character).
Tip 2: I have defined the list of words statically in the code. But I would instead define an external file (e.g. a Json file, or an Xml, or even a simple text file) that you parse dynamically to create the list. That will allow someone else to configure this file with the words/start index/end index without you having to do it in the code each time there is a change.
You could simply check the length of the total string to see if it has the RANDOM and the FOOD attributes before using substring()
String title = "", label = "", tag = "";
if (detail.length() >= 6)
title = detail.substring(0, 6);
if (detail.length() >= 12)
label = detail.substring(6, 12);
if (detail.length() == 16)
tag = detail.substring(12,16);
I would suggest a regex aproach:
public static void main(String[] args) {
String detail = "POTATORANDOMFOODTODAY";
Pattern p = Pattern.compile("(.{0,6})(.{0,6})(.{0,4})(.{0,5})");
Matcher m = p.matcher(detail);
m.find();
String title = m.group(1);
String label = m.group(2);
String tag = m.group(3);
String day = m.group(4);
System.out.println("title: " + title + ", lable: " + label + ", tag: " + tag + ", day: " + day);
}
//output: title: POTATO, lable: RANDOM, tag: FOOD, day: TODAY
If you have a lots of groups I would suggest to use named captured groups. The approach above can particularly be difficult to maintain as adding or removing a group in the middle of the regex upsets the previous numbering used via Matcher#group(int groupNumber). Using named capturing groups:
public static void main(String[] args) {
String detail = "POTATORANDOMFOODTODAY";
Pattern p = Pattern.compile("(?<title>.{0,6})(?<label>.{0,6})(?<tag>.{0,4})(?<day>.{0,5})");
Matcher m = p.matcher(detail);
m.find();
String title = m.group("title");
String label = m.group("label");
String tag = m.group("tag");
String day = m.group("day");
System.out.println("title: " + title + ", lable: " + label + ", tag: " + tag + ", day: " + day);
}
//output: title: POTATO, lable: RANDOM, tag: FOOD, day: TODAY
If the string is dynamic then it can essentially contain basically anything and since there can possibly be no whitespace(s) in the string the only way to know what a specific word (substring) might be is to play the string against a 'word list'. You can quickly come to realize how pivotal even a single whitespace (or separator character) can be within a string. Using the String#substring() method is only good if you already know what all the words within the detail string happen to be.
The simple solution would be to set acceptable rules as to how a specific string should be received. After all, why would you want to accept a string that contains multiple words without a separator character of some type to begin with. If the string has whitespaces in it, to separate the words contained within that string, a mere:
String[] words = string.split("\\s+");
line of code would do the trick. Bottom line, get rid of that nonsense of accepting strings containing multiple words with no separation mechanism included, even if that separation mechanism is by making use of the underscore ( _ ) character (or some other character). Well...if you can.
I suppose sometimes we just can't modify how we're dealt things (something like taxes) and how we receive specific strings is simply out of our control. If this is the case then one way to deal with this dilemma is to work against an established Word-List. This word list can in in the size of a few words to hundreds of thousands of words. The situation you need to deal with will determine the word list size. If small enough the word list can be contained within a String Array or a collection like an ArrayList or List Interface. If really large however then the word list would most likely be contained within a Text file. The word list I most commonly use contains well over 370,000 individual words.
Here is an example of using a small Word-List contained within a List Interface:
String detail = "POTATORANDOMFOODTODAY";
List<String> wordList = Arrays.asList(new String[] {
"pumpkin", "carrot", "potato", "tomato", "lettus", "radish", "bean",
"pea", "food", "random", "today", "yesterday", "tomorrow",
});
// See if the detail string 'contains' any word-list words...
List<String> found = new ArrayList<>();
for (int i = 0; i < wordList.size(); i++) {
String word = wordList.get(i);
if (detail.toLowerCase().contains(word.toLowerCase())) {
found.add(word.toUpperCase());
}
}
/* Ensure the words within the list are in proper order.
That is, the same order as they are received within the
detail String. This is necessary since words from the
word-List can be found anywhere within the detail string. */
int startIndex = 0;
List<String> foundWords = new ArrayList<>();
String tmpStrg = "";
while (!tmpStrg.equals(detail)) {
for (int i = 0; i < found.size(); i++) {
String word = found.get(i);
if (detail.indexOf(word) == startIndex) {
foundWords.add(word);
startIndex = startIndex + word.length();
String procStrg = foundWords.toString().replace(", ", "");
tmpStrg = procStrg.substring(1, procStrg.length() - 1);
}
}
}
//Format and Display the required data
if (foundWords.isEmpty()) {
System.err.println("Couldn't find any required words!");
return; // or whatever...
}
String title = foundWords.get(0);
String label = foundWords.size() > 1 ? foundWords.get(1) : "N/A";
String[] tag = new String[1];
if (foundWords.size() > 2) {
tag = new String[foundWords.size()-2];
for (int i = 0; i < foundWords.size() - 2; i++) {
tag[i] = foundWords.get(i + 2);
}
}
else {
tag[0] = "N/A";
}
System.out.println("Title:\t" + title);
System.out.println("Label:\t" + label);
System.out.println("Tags:\t"
+ Arrays.toString(tag).substring(1, Arrays.toString(tag).length() - 1));
When the above code is run the console window would display:
Title: POTATO
Label: RANDOM
Tags: FOOD, TODAY
You can use the Stream API and use filter() method.
Then you use map() to apply your existing logic, that should do the trick.
Switch-cases could be an alternative but it adds more LoC but reduces the arrow code of all the nested ifs
I have been given an API that saves data in a field in the following order: ["Hello","world","confused"] .
I don't know how to format like this before I pass my data as an input?
The text values are taken from a checkbox.
If I save it in an array I get "[hello,world,confused]" format.
You save the raw values into a single String. YOu have to save it as an array, list or a map. I will not show map usage in this answer. Example:
//Get the Strings
String ex1 = "hello";
String ex2 = "world";
String ex3 = "confused";
//Array
String[] s = new String[3];
s[0] = ex1;
s[1] = ex2;
s[2] = ex3;
//List
List<String> list = new ArrayList<>();
list.add(ex1);
list.add(ex2);
list.add(ex3);
Now, if you send it over the internet and receive the data, send it as raw strings. Then convert it to List for easability. If the amount of Strings vary, this is the easiest way:
StringBuilder sb = new StringBuilder();
sb.append("[\"");
for(int i = 0; i < list.size()/*or .length if it is an array*/; i++){
if(i == list.size() - 1)
sb.append(list.get(i)/*or array[i]*/ + "\"]");
else
sb.append(list.get(i)/*or array[i]*/ + "\",\"");
}
String f = sb.toString();
//Display list: TextView, print to console, whatever you want to do
These are put in a List because it is much easier to loop a list than to manually add every String.
If the amount of Strings are static, you can use String.format:
String f = String.format(Locale.ENGLISH, "[\"%s\",\"%s\",\"%s\"]", ex1, ex2, ex3);
If I somehow misunderstood your question, let me know so I can edit my answer
My question is -
how to convert a String ArrayList to an Integer ArrayList?
I have numbers with ° behind them EX: 352°. If I put those into an Integer ArrayList, it won't recognize the numbers. To solve this, I put them into a String ArrayList and then they are recognized.
I want to convert that String Arraylist back to an Integer Arraylist. So how would I achieve that?
This is my code I have so far. I want to convert ArrayString to an Int Arraylist.
// Read text in txt file.
Scanner ReadFile = new Scanner(new File("F:\\test.txt"));
// Creates an arraylist named ArrayString
ArrayList<String> ArrayString = new ArrayList<String>();
// This will add the text of the txt file to the arraylist.
while (ReadFile.hasNextLine()) {
ArrayString.add(ReadFile.nextLine());
}
ReadFile.close();
// Displays the arraystring.
System.out.println(ArrayString);
Thanks in advance
Diego
PS: Sorry if I am not completely clear, but English isn't my main language. Also I am pretty new to Java.
You can replace any character you want to ignore (in this case °) using String.replaceAll:
"somestring°".replaceAll("°",""); // gives "sometring"
Or you could remove the last character using String.substring:
"somestring°".substring(0, "somestring".length() - 1); // gives "somestring"
One of those should work for your case.
Now all that's left is to parse the input on-the-fly using Integer.parseInt:
ArrayList<Integer> arrayInts = new ArrayList<Integer>();
while (ReadFile.hasNextLine()) {
String input = ReadFile.nextLine();
try {
// try and parse a number from the input. Removes trailing `°`
arrayInts.add(Integer.parseInt(input.replaceAll("°","")));
} catch (NumberFormatException nfe){
System.err.println("'" + input + "' is not a number!");
}
}
You can add your own handling to the case where the input is not an actual number.
For a more lenient parsing process, you might consider using a regular expression.
Note: The following code is using Java 7 features (try-with-resources and diamond operator) to simplify the code while illustrating good coding practices (closing the Scanner). It also uses common naming convention of variables starting with lower-case, but you may of course use any convention you want).
This code is using an inline string instead of a file for two reasons: It shows that data being processed, and it can run as-is for testing.
public static void main(String[] args) {
String testdata = "55°\r\n" +
"bad line with no number\r\n" +
"Two numbers: 123 $78\r\n";
ArrayList<Integer> arrayInt = new ArrayList<>();
try (Scanner readFile = new Scanner(testdata)) {
Pattern digitsPattern = Pattern.compile("(\\d+)");
while (readFile.hasNextLine()) {
Matcher m = digitsPattern.matcher(readFile.nextLine());
while (m.find())
arrayInt.add(Integer.valueOf(m.group(1)));
}
}
System.out.println(arrayInt);
}
This will print:
[55, 123, 78]
You would have to create a new instance of an ArrayList typed with the Integer wrapper class and give it the same size buffer as the String list:
List<Integer> myList = new ArrayList<>(ArrayString.size());
And then iterate through Arraystring assigning the values over from one to the other by using a parsing method in the wrapper class
for (int i = 0; i < ArrayString.size(); i++) {
myList.add(Integer.parseInt(ArrayString.get(i)));
}
I'm trying to read a data file and save the different variables into an array list.
The format of the data file looks a little like this like this
5003639MATH131410591
5003639CHEM434111644
5003639PSYC230110701
Working around the bad formatting of the data file, I added commas to the different sections to make a split work. The new text file created looks something like this
5,003639,MATH,1314,10591
5,003639,CHEM,4341,11644
5,003639,PSYC,2301,10701
After creating said file, I tried to save the information into an array list.
The following is the snippet of trying to do this.
FileReader reader3 = new FileReader("example.txt");
BufferedReader br3 = new BufferedReader(reader3);
while ((strLine = br3.readLine())!=null){
String[] splitOut = strLine.split(", ");
if (splitOut.length == 5)
list.add(new Class(splitOut[0], splitOut[1], splitOut[2], splitOut[3], splitOut[4]));
}
br3.close();
System.out.println(list.get(0));
The following is the structure it is trying to save into
public static class Class{
public final String recordCode;
public final String institutionCode;
public final String subject;
public final String courseNum;
public final String sectionNum;
public Class(String rc, String ic, String sub, String cn, String sn){
recordCode = rc;
institutionCode = ic;
subject = sub;
courseNum = cn;
sectionNum = sn;
}
}
At the end I wanted to print out one of the variables to see that it's working but it gives me an IndexOutOfBoundsException. I wanted to know if I'm maybe saving the info incorrectly, or am I perhaps trying to get it to print out incorrectly?
You have a space in your split delimiter specification, but no spaces in your data.
String[] splitOut = strLine.split(", "); // <-- notice the space?
This will result in a splitOut array of only length 1, not 5 like you expect.
Since you only add to the list when you see a length of 5, checking the list for the 0th element at the end will result in checking for the first element of an empty list, hence your exception.
If you expect your data to have a comma or a space separating the characters then you would alter the split line to be:
String[] splitOut = strLine.split("[, ]");
The split takes a regular expression as an argument.
Rather than artificially adding commas I would look at String.substring in order to cut the line you have read into pieces. For example:
while ((strLine = br3.readLine())!=null) {
if (strLine.length() != 20)
throw new BadLineException("line length is not valid");
list.add(new Class(strLine.substring(0,1), strLine.substring(1,7), strLine.substring(7,11), strLine.substring(11,15), strLine.substring(15,19)));
}
[ Untested: my numbers might be out because I a bit knacked, but you get the idea ]
When I use System.out.println to show the size of a vector after calling the following method then it shows 1 although it should show 2 because the String parameter is "7455573;photo41.png;photo42.png" .
private void getIdClientAndPhotonames(String csvClientPhotos)
{
Vector vListPhotosOfClient = new Vector();
String chainePhotos = "";
String photoName = "";
String photoDirectory = new String(csvClientPhotos.substring(0, csvClientPhotos.indexOf(';')));
chainePhotos = csvClientPhotos.substring(csvClientPhotos.indexOf(';')+1);
chainePhotos = chainePhotos.substring(0, chainePhotos.lastIndexOf(';'));
if (chainePhotos.indexOf(';') == -1)
{
vListPhotosOfClient.addElement(new String(chainePhotos));
}
else // aaa;bbb;...
{
for (int i = 0 ; i < chainePhotos.length() ; i++)
{
if (chainePhotos.charAt(i) == ';')
{
vListPhotosOfClient.addElement(new String(photoName));
photoName = "";
continue;
}
photoName = photoName.concat(String.valueOf(chainePhotos.charAt(i)));
}
}
}
So the vector should contain the two String photo41.png and photo42.png , but when I print the vector content I get only photo41.png.
So what is wrong in my code ?
The answer is not valid for this question anymore, because it has been retagged to java-me. Still true if it was Java (like in the beginning): use String#split if you need to handle csv files.
It's be far easier to split the string:
String[] parts = csvClientPhotos.split(";");
This will give a string array:
{"7455573","photo41.png","photo42.png"}
Then you'd simply copy parts[1] and parts[2] to your vector.
You have two immediate problems.
The first is with your initial manipulation of the string. The two lines:
chainePhotos = csvClientPhotos.substring(csvClientPhotos.indexOf(';')+1);
chainePhotos = chainePhotos.substring(0, chainePhotos.lastIndexOf(';'));
when applied to 7455573;photo41.png;photo42.png will end up giving you photo41.png.
That's because the first line removes everything up to the first ; (7455573;) and the second strips off everything from the final ; onwards (;photo42.png). If your intent is to just get rid of the 7455573; bit, you don't need the second line.
Note that fixing this issue alone will not solve all your ills, you still need one more change.
Even though your input string (to the loop) is the correct photo41.png;photo42.png, you still only add an item to the vector each time you encounter a delimiting ;. There is no such delimiter at the end of that string, meaning that the final item won't be added.
You can fix this by putting the following immediately after the for loop:
if (! photoName.equals(""))
vListPhotosOfClient.addElement(new String(photoName));
which will catch the case of the final name not being terminated with the ;.
These two lines are the problem:
chainePhotos = csvClientPhotos.substring(csvClientPhotos.indexOf(';') + 1);
chainePhotos = chainePhotos.substring(0, chainePhotos.lastIndexOf(';'));
After the first one the chainePhotos contains "photo41.png;photo42.png", but the second one makes it photo41.png - which trigers the if an ends the method with only one element in the vector.
EDITED: what a mess.
I ran it with correct input (as provided by the OP) and made a comment above.
I then fixed it as suggested above, while accidently changing the input to 7455573;photo41.png;photo42.png; which worked, but is probably incorrect and doesn't match the explanation above input-wise.
I wish someone would un-answer this.
You can split the string manually. If the string having the ; symbol means why you can do like this? just do like this,
private void getIdClientAndPhotonames(String csvClientPhotos)
{
Vector vListPhotosOfClient = split(csvClientPhotos);
}
private vector split(String original) {
Vector nodes = new Vector();
String separator = ";";
// Parse nodes into vector
int index = original.indexOf(separator);
while(index>=0) {
nodes.addElement( original.substring(0, index) );
original = original.substring(index+separator.length());
index = original.indexOf(separator);
}
// Get the last node
nodes.addElement( original );
return nodes;
}