Processing an array of data with a pattern in it in Java? - java

So I've got this String with book information:
String data = "Harry Potter 1 | J.K. Rowling| 350 | Fantasy | Hunger Games | Suzanne Collins | 500 | Fantasy | The KingKiller Chronicles | Patrick Rothfuss | 400 | Heroic Fantasy"
Then I split the String:
String splitData = data.split("\\|");
This will cause Harry Potter 1 to be in position 0, J.K. Rowling to be in position 1, 350 to be in position 2, etc.
You might see a pattern in here, which is the fact that at position 0 is a title of a book, at position 1 is the author, at position 2 is the amount of pages and at position 3 is the genre. Then it starts again at position 4, which is again the title of a book, position 5 being the Author of the book, etc etc. I assume that you understand where I'm going.
Now let's say that I want to display all those elements separately, like printing all the titles apart, all the authors, all the amount of pages, etc. How would I accomplish this?
This should be possible to do since the titles are in 0, 4, 8. The authors are in 1, 5, 9, etc.

String data = "Harry Potter 1 | J.K. Rowling| 350 | Fantasy | Hunger Games | Suzanne Collins | 500 | Fantasy | The KingKiller Chronicles | Patrick Rothfuss | 400 | Heroic Fantasy";
String[] splitData = data.split("\\|");
for(int i=0; i<splitData.length;i++) {
if(i % 4 == 0) System.out.println("Title: "+splitData[i]);
else if(i % 4 == 1) System.out.println("Author: "+splitData[i]);
else if(i % 4 == 2) System.out.println("Pages: "+splitData[i]);
else if(i % 4 == 3) System.out.println("Genre: "+splitData[i]);
}
Difficult, isnt it?

You can recall that for loop lets you perform any modifications in the last expression, not only i++. For this case, you can use i += 4. Then in each iteration the name will ne at splitData[i], the author at splitData[i+1], the number of pages at splitData[i+2], and the genre at splitData[i+3].

Related

How would I go about removing indexes that come before (not after!) an index stated by the user in Java using ArrayLists?

I've been working on a program that lets you enter pizza toppings. It's pretty much complete, except for the removeToppings() method.
The problem is, I can't seem to figure out the correct way to remove the toppings in the index that the user chooses and all of the toppings BEFORE it. I have found numerous ways to remove indexes that come AFTER, but even then I can't find ways to reverse them.
Here's the method in question:
public static void removeTopping()
{
Scanner in = new Scanner(System.in);
printPizza();
System.out.println("What topping do you want to remove?\n"
+ "Keep in mind, this loses everything else above it.\n"
+ "Enter index number: ");
int remove = in.nextInt();
toppings.subList(remove, toppings.size()).clear(); //this is the problem line!
}
The printPizza() method would print something that looks like this:
|index|topping|
| 0 | cheese
| 1 | three
| 2 | cheese
| 3 | two
| 4 | cheese
| 5 | one
| 6 | sauce
| 7 | crust
Say I enter 5, the program would remove indexes 5, 6, and 7. I would want it to remove 0-5. Any pointers would be much appreciated.
You can use a for loop to achieve this.
The loop will start by removing the index specified by the remove variable. Then, the loop will decrement i until it reaches -1, by this point, it will have removed every element below the index you set.
for (int i = remove; i > -1; i--) {
myList.remove(i);
}
If in your example you also want to remove the position 5:
toppings.subList(0, remove + 1).clear();

Java Recursion - reverse order integers from an array, how it works?

public static void reversePrint(int[] numbers){
if(numbers.length == 0) //Base case
return;
int[] a = new int[numbers.length-1];
for(int i = 0; i < numbers.length-1;i++)
a[i] = numbers[i+1];
reversePrint(a);
System.out.println(numbers[0]);
}
public static void main(String[] args){
int[] array = new int[]{5,1,34,12,7};
reversePrint(array);
}
Output:
7
12
34
1
5
Everything is pretty straightforward, have a function called reversePrint. When you pass those numbers to the function reversePrint, it takes the first value out, (in this case '5') and then it calls reversePrint again,now with the smaller list.
This Continues until finally we're left with no more numbers, and begins to print them out.
My confusion is in line '10', if the list of numbers is getting less and less by removing the first number each time, how does calling 'System.out.println(numbers[0]);' retrieve numbers that have been removed from the list, and doing so in reverse order?
Here's a scheme to understand the stack of calls in this recursion:
reversePrint([5,1,34,12,7]) {
reversePrint([1,34,12,7]) { // <-- this list IS A COPY, it just ignores the first number
reversePrint([34,12,7]) {
reversePrint([12,7]) {
reversePrint([7]) {
reversePrint([]);
print(7); // <-- this is the first number of the current list
};
print(12);
};
print(34);
};
print(1);
};
print(5);
};
As you can see, the System.out.println(numbers[0]) is called AFTER propagating the recursion. Note that a new array is created in each call, you don't lose the first number.
First, you don't actually remove numbers: you copy them from numbers to a skipping the one in position 0. That System.out.println prints from numbers, so the integer at index 0 will still be the same.
Second, the System.out.println statement is after the recursive call, so it will be executed after that call returns. So basically, the first System.out.println that will execute will be the one in the last call:
for ...
reversePrint
|
| for ...
| reversePrint
| |
| | for ...
| | reversePrint
| | |
| | | for ...
| | | reversePrint
| | | |
| | | | for ...
| | | | reversePrint
| | | | |
| | | | | return
| | | | |
| | | | System.out.println
| | | |
| | | System.out.println
| | |
| | System.out.println
| |
| System.out.println
|
System.out.println
That's an exceptionally ineffective implementation of
Arrays.asList(5, 1, 34, 12, 7).reverse().forEach(System.out::println)
But to answer your question, the reversePrint creates a new array with the first item removed from the array, then prints out the first of the original. The second call will receive [1, 34, 12, 7] because the first has removed the 5, so it will print out the 1.
how does calling 'System.out.println(numbers[0]);' retrieve numbers that have been removed from the list, and doing so in reverse order?
No numbers have been removed from any array in this code. Each recursive call creates a new array and copies to it all the elements of the current array except of the first element.
Therefore the array passed to the i'th call to reversePrint() contains the last n-i+1 elements of the original array.
The recursion ends when reversePrint() is called for an empty array. When the last recursive call returns, the next to last call prints numbers[0], which contains the last element of the original array. Then the previous reversePrint() prints numbers[0], which contains the next to last element of the original array, and so on...
These are the recursive calls:
reversePrint({5,1,34,12,7})
reversePrint({1,34,12,7})
reversePrint({34,12,7})
reversePrint({12,7})
reversePrint({7})
reversePrint({})
Now, after each of them returns, numbers[0] is printed, so you get
7
12
34
1
5
Perhaps doing it the classic way (rather than making a copy of the array as you do) it would be clearer what is happening.
// Private version to pass the offset which increases on each call.
private static void reversePrint(int[] numbers, int from){
// Base case - stop at end of array.
if(numbers.length > from) {
// Print everything else first.
reversePrint(numbers, from+1);
// Then the one I am at.
System.out.println(numbers[from]);
}
}
public static void reversePrint(int[] numbers){
reversePrint(numbers, 0);
}
public void test() throws Exception {
System.out.println("Hello world!");
int[] array = new int[]{5,1,34,12,7};
reversePrint(array);
}

REGEX not working as in JAVA program as expected

I have been working on a program which makes use of Regular Expressions. It searches for some text in the files to give me a database based on the scores of different players.
Here is the sample of the text within which it searches.
ISLAMABAD UNITED 1st innings
Player Status Runs Blls 4s 6s S/R
David Warner lbw b. Hassan 19 16 4 0 118.8%
Joe Burns b. Morkel 73 149 16 0 49.0%
Kane Wiliiamson b. Tahir 135 166 28 2 81.3%
Asad Shafiq c. Rahane b. Morkel 22 38 5 0 57.9%
Kraigg Braithwaite c. Khan b. Boult 24 36 5 0 66.7%
Corey Anderson b. Tahir 18 47 3 0 38.3%
Sarfaraz Ahmed b. Morkel 0 6 0 0 0.0%
Tim Southee c. Hales b. Morkel 0 6 0 0 0.0%
Kyle Abbbott c. Rahane b. Morkel 26 35 4 0 74.3%
Steven Finn c. Hales b. Hassan 10 45 1 0 22.2%
Yasir Shah not out 1 12 0 0 8.3%
Total: 338/10 Overs: 92.1 Run Rate: 3.67 Extras: 10
Day 2 10:11 AM
-X-
I am using the following regex to get the different fields..
((?:\/)?(?:[A-Za-z']+)?\s?(?:[A-Za-z']+)?\s?(?:[A-Za-z']+)?\s?)\s+(?:lbw)?(?:not\sout)?(?:run\sout)?\s?(?:\(((?:[A-Za-z']+)?\s?(?:['A-Za-z]+)?)\))?(?:(?:st\s)?\s?(?:((?:['A-Za-z]+)\s(?:['A-Za-z]+)?)))?(?:c(?:\.)?\s((?:(?:['A-Za-z]+)?\s(?:[A-Za-z']+)?)?(?:&)?))?\s+(?:b\.)?\s+((?:[A-Za-z']+)\s(?:[A-Za-z']+)?)?\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)
Batsman Name - Group 1
Person Affecting Stumping (if any) - Group 2
Person Affecting RunOut (if any) - Group 3
Person Taking Catch (if any) - Group 4
Person Taking the wicket (if any) - Group 5
Runs Scored - Group 6
Balls Faced - Group 7
Fours Hit - Group 8
Sixes Hit - Group 9
Here is an example of the text I need to extract...
Group 0 contains David Warner lbw b. Hassan 19 16 4 0 118.8%
Group 1 contains 'David Warner'
Group 2 does not exist in this example
Group 3 does not exist in this example
Group 4 does not exist in this example
Group 5 contains 'Hassan'
Group 6 contains '19'
Group 7 contains '16'
Group 8 contains '4'
Group 9 contains '0'
When I try this on Regexr or Regex101, it gives the Group 1 as David Warner in the Group 1... But in my Java Program, it gives it as David. It is same for all results. I don't know why?
Here's the code of my program:
Matcher bat = Pattern.compile("((?:\\/)?(?:[A-Za-z']+)?\\s?(?:[A-Za-z']+)?\\s?(?:[A-Za-z']+)?\\s?)\\s+(?:lbw)?(?:not\\sout)?(?:run\\sout)?\\s?(?:\\(((?:[A-Za-z']+)?\\s?(?:['A-Za-z]+)?)\\))?(?:(?:st\\s)?\\s?(?:((?:['A-Za-z]+)\\s(?:['A-Za-z]+)?)))?(?:c(?:\\.)?\\s((?:(?:['A-Za-z]+)?\\s(?:[A-Za-z']+)?)?(?:&)?))?\\s+(?:b\\.)?\\s+((?:[A-Za-z']+)\\s(?:[A-Za-z']+)?)?\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)").matcher(batting.group(1));
while (bat.find()) {
batPos++;
Batsman a = new Batsman(bat.group(1).replace("\n", "").replace("\r", "").replace("S/R", "").replace("/R", "").trim(), batting.group(2));
if (bat.group(0).contains("not out")) {
a.bat(Integer.parseInt(bat.group(6)), Integer.parseInt(bat.group(7)), Integer.parseInt(bat.group(8)), Integer.parseInt(bat.group(9)), batting.group(2), false);
} else {
a.bat(Integer.parseInt(bat.group(6)), Integer.parseInt(bat.group(7)), Integer.parseInt(bat.group(8)), Integer.parseInt(bat.group(9)), batting.group(2), true);
}
if (!teams.contains(batting.group(2))) {
teams.add(batting.group(2));
}
boolean f = true;
Batsman clone = null;
for (Batsman b1 : batted) {
if (b1.eq(a)) {
clone = b1;
f = false;
break;
}
}
if (!f) {
if (bat.group(0).contains("not out")) {
clone.batUpdate(a.getRunScored(), a.getBallFaced(), a.getFour(), a.getSix(), false, true);
} else {
clone.batUpdate(a.getRunScored(), a.getBallFaced(), a.getFour(), a.getSix(), true, true);
}
} else {
batted.add(a);
}
}
Your regex is way too complicated for such a simple task. To make it simple(or eliminate it for that matter), operate on a single line rather than the bunch of text.
For this, do
String array[] = str.split("\\n");
Then once you get each individual line, just split by a mutliple spaces, like
String parts[] = array[1].split("\\s\\s+");
Then you can access each part seperately, like Status can be accessed like
System.out.println("Status - " + parts[1]);
All commentators are right, of course, this might not be a typical problem to solve with a regex. But to answer your question - why is there a difference between java and regex101? - let's try to pull out some of the problems caused by your regex that makes it too complex. Next step would be to track down if and why there is a difference in using it in java.
I tried to understand your regex (and cricket at the same time!) and came up with a proposal that might help you to make us understand what your regex should look like.
First attempt reads until the number columns are reached. My guess is, that you should be looking at alternation instead of introducing a lot of groups. Take a look at this: example 1
Explanation:
( # group 1 start
\/? # not sure why there should be /?
[A-Z][a-z]+ # first name
(?:\s(?:[A-Z]['a-z]+)+) # last name
)
(?:\ # spaces
( # group 2 start
lbw # lbw or
|not\sout # not out or
|(c\.|st|run\sout) # group 3: c., st or run out
\s # space
\(? # optional (
(\w+) # group 4: name
\)? # optional )
))? # group 2 end
(?:\s+ # spaces
( # group 5 start
(?:b\.\s)(\w+) # b. name
))? # group 5 end
\s+ # spaces
EDIT 1: Actually, there is a 'stumped' option missing in your regex as well. Added that in mine.
EDIT 2: Stumped doesn't have a dot.
EDIT 3: The complete example can be found at example 2
Some java code to test it:
import java.util.regex.Matcher;
import java.util.regex.Pattern;
class Foo {
public static void main(String[] args) {
String[] examples = {
"David Warner lbw b. Hassan 19 16 4 0 118.8%",
"Joe Burns b. Morkel 73 149 16 0 49.0%",
"Asad Shafiq c. Rahane b. Morkel 22 38 5 0 57.9%",
"Yasir Shah not out 1 12 0 0 8.3%",
"Yasir Shah st Rahane 1 12 0 0 8.3%",
"Morne Morkel run out (Shah) 11 17 1 1 64.7%"
};
Pattern pattern = Pattern.compile("(\\/?[A-Z][a-z]+(?:\\s(?:[A-Z]['a-z]+)+))(?:\\s+(lbw|not\\sout|(c\\.|st|run\\sout)\\s\\(?(\\w+)\\)?))?(?:\\s+((?:b\\.\\s)(\\w+)))?\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+\\.\\d%)");
for (String text : examples) {
System.out.println("TEXT: " + text);
Matcher matcher = pattern.matcher(text);
if (matcher.matches()) {
System.out.println("batsman: " + matcher.group(1));
if (matcher.group(2) != null) System.out.println(matcher.group(2));
if (matcher.group(5) != null && matcher.group(5).matches("^b.*"))
System.out.println("bowler: " + matcher.group(6));
StringBuilder sb = new StringBuilder("numbers are: ");
int[] groups = {7, 8, 9, 10, 11};
for (int i : groups) {
sb.append(" " + matcher.group(i));
}
System.out.println(sb.toString());
System.out.println();
}
}
}
}

Why is my recursive maze algorithm not back tracking?

Forgive me i'm new to recursion but as far as i understand this should work, but it doesn't. I made this method that calls itself recursively when successfully found a path:
private void RandStep(Point pos)
{
Collections.shuffle(Arrays.asList(directions), rand); //Incorrect, should have a local directions array. Nothing to do with the question though.
for (int i = 0; i < directions.length;i++)
{
//Check if the positions are within map bounds.
if (pos.x + directions[i].x >= 0 && pos.x + directions[i].x < width && pos.y + directions[i].y >= 0 && pos.y + directions[i].y < height)
{
//Check if the position is unvisited.
if (!mazeMap[pos.x + directions[i].x][pos.y + directions[i].y].visited)
{
//Break walls this tile.
CarvePassage(pos, directions[i]);
mazeMap[pos.x + directions[i].x][pos.y + directions[i].y].visited = true;
position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);
RandStep(position);
}
}
}
}
First it randomizes an array with 4 directions.
Then i loop through the array to find a possible direction.
Then It checks if the direction found is valid otherwise it goes to the next direction in the array
When it is valid it calls another method that carves the wall of the current tile and the next tile.
It changes the current position to the next and sets it's flag to visited.
Finally it calls itself again to make the next step.
This all works until the first time it gets stuck between visited cells or map bounds. If i understand recursion correctly it should exit this method and go to the previous run of RandStep and finish the direction loop. When it does not find any valid cells there: it again should exit and finish the loop in the previous RandStep. This should be repeated until it finished the direction loop of the very first RandStep run.
Like i said, it stops at the moment it cannot find any valid cells. It does not continue the previous methods on the recursion stack.
Quite simply, it's because you don't step back!
position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);
RandStep(position);
Should be something like:
position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y);
RandStep(position);
position.setLocation(pos.x - directions[i].x, pos.y - directions[i].y);
As a bit of intuition, think about what happens in the base case of recursion. all tiles around you are visited, and you are at a dead end. That situation looks like:
_
| |
|x|
(x = "you are here")
Then, position.setLocation(pos.x + directions[i].x, pos.y + directions[i].y); puts you here:
_
|x|
| |
Then, RandStep(position); does nothing since all locations around you are explored. So the next thing you want to do is step backwards, which is accomplished by something like: position.setLocation(pos.x - directions[i].x, pos.y - directions[i].y);.
Let's take a closer look at what is happening under the hood. Assuming you are familiar with what a heap and a stack are, the variable position resides on the heap, as it is not local to your function RandStep. On the other hand, pos is local, so it resides on the stack. With every recursive call, a new stack frame is allocated (from frameA to frameC). I represent positions as single values for the sake of brevity. Let's say that you start from position 0, and then break the walls towards position 1. What happens is that a new frame is allocated for the new value of pos, but positionis overwritten. The same when you transition from position 1 to position 2. Let's say that from position 2 there's nowhere to go, so we need to trace back. Then frameC is deallocated. However, instead of using the value of pos in the top frame (which is frameB now), your call to RandStep was made on position, so the value that you use is the one on the heap, which remained unchanged despite the frame deallocation.
"Stepping back" as suggested by Intredasting acutally means manually updating the heap, so that position follows the same evolution as pos. In my opinion, that defeats the purpose of recursion. Its beauty is that it takes care itself of the stepping back part by deallocation.
In conclusion, what you need to do is avoid changing the heap at all in recursive functions.
|STACK | |STACK | |STACK |
| | | | | |
| | | | |frameC: pos = 2|---> got stuck, deallocate frame
| | |frameB: pos=1 | |frameB: pos = 1| and backtrack
|frameA: pos=0 | |frameA: pos=0 | |frameA: pos = 0|
|---------------| |--------------| |---------------|
|HEAP | |HEAP | |HEAP |
|position = 0 | |position=1 | |position = 2 |
| | | | | |
| | | | | |
----------------- ---------------- -----------------

Adding ArrayList to TreeMap within a while cycle

I'm retrieving data from a table in a database and add the whole row to a TreeMap.
I'm using the following code:
ArrayList<String> listaValores = new ArrayList();
TreeMap<Integer, ArrayList> tmValores = new TreeMap<>();
(...)
while (rs.next())
{
listaValores.clear();
for(int i=2; i<=ncolunas; i++)
{
listaValores.add(rs.getString(i));
}
tmValores.put(rs.getInt(1), listaValores);
}
My TreeMap keys are inserted fine but the values are always repeated as the values from the last line as a result from the SELECT * FROM Table query.
So if my table is:
id | name | last name |
1 | joe | lewis |
2 | mark | spencer |
3 | mike | carter |
4 | duke | melvin |
My TreeMap contains:
1=> duke, melvin
2=> duke, melvin
3=> duke, melvin
4=> duke, melvin
Instead of (as I wanted)
1=> joe, lewis
2=> mark, spencer
3=> mike, carter
4=> duke melvin
Can anyone point me out where is the problem?
I believe you have to reassign the value of listaValores.
Simply change
listaValores.clear();
to
listaValores = new ArrayList<String>();
Objects in Java are passed around as references, so you are in fact adding the same list for all the keys in your map.
Since you clear it at every step and then add some values to it, it will contain just the last row, after the while loop has finished.
What you really want to do is to create an instance of ArrayList for every row and add that to your map, instead of clearing the values in the old one:
while (rs.next())
{
listaValores = new ArrayList<String>();
for(int i = 2; i <= ncolunas; i++)
{
listaValores.add(rs.getString(i));
}
tmValores.put(rs.getInt(1), listaValores);
}

Categories

Resources