Looping through 2d array with String.split() - java

I've got a simple problem, but I'm new to Java coming from PHP. I need to split a delimited text file into an array. I've broken it down into an array of lines, each one would look something like this:
{
{Bob | Smithers | Likes Cats | Doesnt Like Dogs},
{Jane | Haversham | Likes Bats | Doesnt Like People}
}
I need to turn this into a 2 dimensional array.
In PHP, it's a cinch. You just use explode(); I tried using String.split on a 1d array and it wasn't that bad either. The things is, I haven't yet learned how to be nice to Java. So I don't know how to loop through the array and turn it into a 2d. This is what I have:
for (i = 0; i < array.length; i++) {
String[i][] 2dArray = array[i].split("|", 4);
}
PHP would be
for ($i = 0; $i < count($array); $i++) {
$array[i][] = explode(",", $array[i]);
}

You can loop the array like this:
// Initialize array
String[] array = {
"Bob | Smithers | Likes Cats | Doesnt Like Dogs",
"Jane | Haversham | Likes Bats | Doesnt Like People"
};
// Convert 1d to 2d array
String[][] array2d = new String[2][4];
for(int i=0;i<array.length;i++) {
String[] temp = array[i].split(" \\| ");
for(int j=0;j<temp.length;j++) {
array2d[i][j] = temp[j];
}
}
// Print the array
for(int i=0;i<array2d.length;i++) {
System.out.println(Arrays.toString(array2d[i]));
}
Notes: I used \\|to split the pipe character.

Problem
If I got you right you have an input like this:
{{Bob | Smithers | Likes Cats | Doesnt Like Dogs},{Jane | Haversham | Likes Bats | Doesnt Like People}}
Readable version:
{
{Bob | Smithers | Likes Cats | Doesnt Like Dogs},
{Jane | Haversham | Likes Bats | Doesnt Like People}
}
And you want to represent that structure in a 2-dimensional String aray, String[][].
Solution
The key is the method String#split which splits a given String into substrings delimited by a given symbol. This is , and | in your example.
First of all we remove all {, } as we don't need them (as long as the text itself does not contain delimiter):
String input = ...
String inputWithoutCurly = input.replaceAll("[{}]", "");
The text is now:
Bob | Smithers | Likes Cats | Doesnt Like Dogs,Jane | Haversham | Likes Bats | Doesnt Like People
Next we want to create the outer dimension of the array, that is split by ,:
String[] entries = inputWithoutCurly.split(",");
Structure now is:
[
"Bob | Smithers | Likes Cats | Doesnt Like Dogs",
"Jane | Haversham | Likes Bats | Doesnt Like People"
]
We now want to split each of the inner texts into their components. We therefore iterate all entries, split them by | and collect them to the result:
// Declaring a new 2-dim array with unknown inner dimension
String[][] result = new String[entries.length][];
// Iterating all entries
for (int i = 0; i < entries.length; i++) {
String[] data = entries[i].split(" | ");
// Collect data to result
result[i] = data;
}
Finally we have the desired structure of:
[
[ "Bob", "Smithers", "Likes Cats", "Doesnt Like Dogs" ],
[ "Jane", "Haversham", "Likes Bats", "Doesnt Like People"]
]
Everything compact:
String[] entries = input.replaceAll("[{}]", "").split(",");
String[][] result = new String[entries.length][];
for (int i = 0; i < entries.length; i++) {
result[i] = entries[i].split(" | ");
}
Stream
If you have Java 8 or newer you can use the Stream API for a compact functional style:
String[][] result = Arrays.stream(input.replaceAll("[{}]", "").split(","))
.map(entry -> entry.split(" | "))
.toArray(String[][]::new);

Related

reading data from a file and printing the only specific instances of the data

I am trying to manipulate this exercise but I am having a hard time executing it. Input is being scanned from a file. The information is then being formatted as its being output.
The csv file currently has the following information:
16:40,Wonders of the World,G
20:00,Wonders of the World,G
19:00,End of the Universe,NC-17
12:45,Buffalo Bill And The Indians or Sitting Bull's History Lesson,PG
15:00,Buffalo Bill And The Indians or Sitting Bull's History Lesson,PG
19:30,Buffalo Bill And The Indians or Sitting Bull's History Lesson,PG
10:00,Adventure of Lewis and Clark,PG-13
14:30,Adventure of Lewis and Clark,PG-13
19:00,Halloween,R
But my output is coming out like this:
Wonders of the World | G | 16:40
Wonders of the World | G | 20:00
End of the Universe | NC-17 | 19:00
Buffalo Bill And The Indians or Sitting Bull | PG | 12:45
Buffalo Bill And The Indians or Sitting Bull | PG | 15:00
Buffalo Bill And The Indians or Sitting Bull | PG | 19:30
Adventure of Lewis and Clark | PG-13 | 10:00
Adventure of Lewis and Clark | PG-13 | 14:30
Halloween | R | 19:00
I need to be able to only output one movie with showtimes so it looks like this.
Wonders of the World | G | 16:40 20:00
End of the Universe | NC-17 | 19:00
Buffalo Bill And The Indians or Sitting Bull | PG | 12:45 15:00 19:30
Adventure of Lewis and Clark | PG-13 | 10:00 14:30
Halloween | R | 19:00
My code so far:
public class LabProgram4 {
public static void main(String[] args) throws IOException {
String filename = "movies.csv";
int recordCount = 0;
Scanner fileScanner = new Scanner(new File(filename));
while (fileScanner.hasNextLine()) {
fileScanner.nextLine();
++recordCount;
}
String[] showtimes = new String[recordCount];
String[] title = new String[recordCount];
String[] rating = new String[recordCount];
fileScanner.close();
fileScanner = new Scanner(new File(filename));
for (int i = 0; i < recordCount; ++i) {
String[] data = fileScanner.nextLine().strip().split(",");
showtimes[i] = data[0].strip();
title[i] = data[1].strip();
rating[i] = data[2].strip();
}
fileScanner.close();
for (int i = 0; i < recordCount; ++i) {
if (title[i].length() > 44)
title[i] = title[i].substring(0, 44);
System.out.printf("%-44s | %5s | %s\n", title[i], rating[i], showtimes[i]);
}
}
}
public static final class Movie {
private String title;
private String showTime;
private String rating;
}
public static void main(String... args) throws FileNotFoundException {
List<Movie> movies = readMovies(new File("d:/movies.csv"));
Map<String, List<Movie>> map = movies.stream().collect(Collectors.groupingBy(movie -> movie.title));
print(map);
}
private static void print(Map<String, List<Movie>> map) {
int titleWidth = getTitleWidth(map);
int ratingWidth = getRatingWidth(map);
map.forEach((title, movies) -> {
String rating = movies.stream().map(movie -> movie.rating).distinct().collect(Collectors.joining(" "));
String showTime = movies.stream().map(movie -> movie.showTime).distinct().sorted().collect(Collectors.joining(" "));
System.out.format("%-" + titleWidth + "s | %-" + ratingWidth + "s | %s\n", title, rating, showTime);
});
}
private static int getTitleWidth(Map<String, List<Movie>> map) {
return map.keySet().stream()
.mapToInt(String::length)
.max().orElse(0);
}
private static int getRatingWidth(Map<String, List<Movie>> map) {
return map.values().stream()
.mapToInt(movies -> movies.stream()
.map(movie -> movie.rating)
.distinct()
.mapToInt(String::length)
.sum())
.max().orElse(0);
}
private static final int SHOW_TIME = 0;
private static final int TITLE = 1;
private static final int RATING = 2;
private static List<Movie> readMovies(File file) throws FileNotFoundException {
try (Scanner scan = new Scanner(file)) {
List<Movie> movies = new ArrayList<>();
while (scan.hasNext()) {
String[] data = scan.nextLine().split(",");
Movie movie = new Movie();
movie.title = data[TITLE].trim();
movie.showTime = data[SHOW_TIME].trim();
movie.rating = data[RATING].trim();
movies.add(movie);
}
return movies;
}
}
In my opinion, reading a file once for the sole purpose of getting the number of records (per say) is just a wrong way to go. Read the file once and carry out the task as the file is being read.
There are oodles of ways to read a file and store or display records in a unique fashion (such as no duplicate titles). Using parallel arrays to store the data is one way I suppose but these arrays need to be initialized to a specific length since they can not grow dynamically. Although not impossible, this is rather problematic in this particular situation and would require far more code to carry out the task compared to utilizing a Collection object such as a List Interface, ArrayList, (etc) which can grow dynamically.
The code below utilizes the java.util.List Interface to store and then later display Movies read in from the movies.csv file. The code looks long but it is mostly comments explaining things. I would suggest you read those comments and if you like delete them since they are excessive:
// The Movies data file name.
String filename = "movies.csv";
// Counter to keep track of the number of movies stored.
int moviesCount = 0;
// List Interface object to store movie titles in.
java.util.List<String> movies = new java.util.ArrayList<>();
// 'Try With Resources' used here to auto-close the reader
try (Scanner reader = new Scanner(new File(filename))) {
// Read the data file line by line.
String dataLine;
while (reader.hasNextLine()) {
dataLine = reader.nextLine().trim();
// Skip blank lines...
if (dataLine.isEmpty()) {
continue;
}
/* The regex ("\\s*,\\s*") passed to the String#split() method
below handles any number of whitespaces before or after the
comma delimiter on any read in data file line. */
String[] data = dataLine.split("\\s*,\\s*");
/* Do we already have title in the 'movies' List?
If so just add the show-time to the title and
continue on to the next file data line. */
boolean alreadyHave = false; // Flag that we don't already have this title
for (int i = 0; i < movies.size(); i++) {
// Do we already have the movie title in the list?
if (movies.get(i).contains(data[1])) {
// Yes we do so flag it that we already do have this title.
alreadyHave = true;
// Add the additional show-time to that title's stored information
movies.set(i, movies.get(i) + " " + data[0]);
/* Break out of this 'for' loop since there is no
more need to check other titles in the List. */
break;
}
}
/* If however we don't already have this movie title
in the List then add it in the desired display
format using the Pipe (|) character as a delimiter. */
if (!alreadyHave) {
moviesCount++; // Increment the number of movies
movies.add(String.format("%s | %s | %s", data[1], data[2], data[0]));
}
}
}
// DISPLAY THE MOVIES LIST IN TABLE STYLE FASHION
// Display Title in Console Window:
String msg = "There are " + moviesCount + " movies with various show-times:";
System.out.println(msg); // Print title
// Display Table Header:
String header = String.format("%-44s | %6s | %s", "Movie Title", "Rating", "Show Times");
String overUnderline = String.join("", java.util.Collections.nCopies(header.length(), "="));
// Header Overline
System.out.println(overUnderline);
System.out.println(header);
// Header Underline
System.out.println(overUnderline);
// Display the movies in console window.
for (String movie : movies) {
/* Split the current List element into its respective parts
using the String#split() method so that the List contents
can be displayed in a table format. The regex passed t0
the 'split()' method ("\\s*\\|\\s*") will take care of any
whitespaces before or after any Pipe (|) delimiter so that
String#trim() or String#strip() is not required. Note that
the Pipe (|) delimiter needs to be escaped (\\|) within the
expression so as to acquire is literal meaning since it is
a regex meta character. */
String[] movieParts = movie.split("\\s*\\|\\s*");
/* 'Ternary Operators' are used in the String#format() data
section components so to truncate movie Title Names to the
desire table cell width of 44 and to convert Rating and
Show-Times to "N/A" should they EVER be empty (contain no
data). */
System.out.println(String.format("%-44s | %6s | %s",
(movieParts[0].length() > 44 ? movieParts[0].substring(0, 44) : movieParts[0]),
(movieParts[1].isEmpty() ? "N/A" : movieParts[1]),
(movieParts[2].isEmpty() ? "N/A" : movieParts[2])));
}
System.out.println(overUnderline);
If the data file actually contains what you've indicated in your post then the code above will display the following into the Console Window:
There are 5 movies with various show-times:
==================================================================
Movie Title | Rating | Show Times
==================================================================
Wonders of the World | G | 16:40 20:00
End of the Universe | NC-17 | 19:00
Buffalo Bill And The Indians or Sitting Bull | PG | 12:45 15:00 19:30
Adventure of Lewis and Clark | PG-13 | 10:00 14:30
Halloween | R | 19:00
==================================================================

Replace content of each files with it's header values in spark

I have a directory with several text files and I access that all files in spark as follows,
JavaRDD<String> filesRDD = sc.textFile(directoryName);
In each file, the first line is a header which contains some mapping values. eg:-
"1,apple|4,banana|3,lemon"
that means if, in the content, there is a "3", it maps to "lemon".
Example of the content as follows,
I like 1
John eat 3 and 1
and so on.
Now What I need to do is, I need to filter lines from the content first and assign original values from the mapping. For example, the first filter by the string "like" and I get "I like 1" then, I replace with mapping, then "I like apple"
Please note that this mapping header is different from each file. How can I do this? Since I'm new to spark, I don't have much idea on how to achieve this.
Do you want something like this?
var fruitPair = sc.parallelize(List("1,apple","4,banana","3,lemon")).map{ str =>
var temp = str.split(",")
(temp(0), temp(1))
}
fruitPair.toDF.show()
+---+------+
| _1| _2|
+---+------+
| 1| apple|
| 4|banana|
| 3| lemon|
+---+------+
var contents = List("I like 1", "John eat 3 and 1")
var results = contents.map { content =>
var tmpContent = content
fruitPair.collect.foreach { item =>
var index = tmpContent.indexOf(item._1)
if (index >= 0) {
tmpContent = tmpContent.replace(item._1, item._2)
}
}
tmpContent
}
results.foreach{ it => println(it) }
I like apple
John eat lemon and apple
results: List[String] = List(I like apple, John eat lemon and apple)

Cucumber Java - Iterate through list of list in Java

I have my scenerio file like this
Scenario: Login Page with Valid credentials
Given user is on Application landing page
Then we verify following user exists
| name | email | phone |
| Shankar | san#email.com | 999 |
| Ram | ram#email.com | 888 |
| Sham | sham#email.org | 666 |
In my step definition, I want to iterate using for and foreach loop. I tried using while loop. It is working fine. Can anyone tell me how to iterate using for and foreach loop?
#Then("^we verify following user exists$")
public void we_verify_following_user_exists(DataTable datatable)
throws Throwable {
List<List<String>> data = datatable.raw();
Iterator<List<String>> it = data.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
And I'm expecting an output like below
name,email,phone
Shankar,san#email.com,999
Ram,ram#email.com,888
Sham,sham#email.org,666
you can iterate with for loops similar to:
for (List<String>currentData : data) {
org.apache.commons.lang3.StringUtils.join(currentData, ",")
System.out.println(System.getProperty("line.separator"));
}
Try this:
#When("^User enters below credentials$")
public void user_enters_below_credentials(DataTable credentials) throws Throwable {
List<List<String>> list = credentials.raw();
for (int i = 0; i < list.size(); i++) {
for (int j = 0; j < 4; j++) {
System.out.println(list.get(i).get(j));
}
}
}
This should work for you
for(List<String> list: data){
System.out.println(list.toString());
}

what is the best way to find the topper by subject?

I have a string array of student info:
StudentNumber Integer, Subject String, mmarks integer
What would be the best way to use the java & collection to find out the topper in each subject.
ArrayList<String> strings = new ArrayList<String>();
strings.add("1 | Computers | 48");
strings.add("2 | Data Structures | 89");
strings.add("33 | English | 35");
strings.add("24 | Maths | 70");
strings.add("15 | Computers | 58");
strings.add("6 | Data Structures | 55");
strings.add("7 | English | 40");
strings.add("18 | Maths | 73");
for (String str : strings) {
String [] strArray = str.split("\\|");
// in completed code
for (int i = 0; i < str.length(); i++) {
sam.put(strArray[0], strArray[2]);
s.add(strArray[1]);
}
}
Expected output
15 Computers
2 Data structures
7 English
18 Maths
Create a Result class to store the information:
class Result {
private int studentNumber;
private String subject;
private int mark;
// constructor, getters and setters go here ...
}
Now convert your List<String> to a List<Result>:
List<Result> results = new ArrayList<>();
for (String s : strings){
String[] sa = s.split(" \\| ");
results.add(new Result(Integer.parseInt(sa[0]), sa[1], Integer.parseInt(sa[2])));
}
Create a stream from the results list, group by subject, and find the student with the highest mark:
Map<String, Integer> map = results.stream()
.collect(Collectors.groupingBy(Result::getSubject,
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Result::getMark)), r -> r.get().getStudentNumber())));
Print the result:
map.forEach((k,v) -> System.out.println(v + " " + k));
15 Computers
18 Maths
7 English
2 Data Structures

String tokenizing to remove some data

I have a string like this:
1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | | | | |
The string might have more/less data also.
I need to remove | and get only numbers one by one.
Guava's Splitter Rocks!
String input = "1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | | | | |";
Iterable<String> entries = Splitter.on("|")
.trimResults()
.omitEmptyStrings()
.split(input);
And if you really want to get fancy:
Iterable<Integer> ints = Iterables.transform(entries,
new Function<String, Integer>(){
Integer apply(String input){
return Integer.parseInt(input);
}
});
Although you definitely could use a regex method or String.split, I feel that using Splitter is less likely to be error-prone and is more readable and maintainable. You could argue that String.split might be more efficient but since you are going to have to do all the trimming and checking for empty strings anyway, I think it will probably even out.
One comment about transform, it does the calculation on an as-needed basis which can be great but also means that the transform may be done multiple times on the same element. Therefore I recommend something like this to perform all the calculations once.
Function<String, Integer> toInt = new Function...
Iterable<Integer> values = Iterables.transform(entries, toInt);
List<Integer> valueList = Lists.newArrayList(values);
You can try using a Scanner:
Scanner sc = new Scanner(myString);
sc.useDelimiter("|");
List<Integer> numbers = new LinkedList<Integer>();
while(sc.hasNext()) {
if(sc.hasNextInt()) {
numbers.add(sc.nextInt());
} else {
sc.next();
}
}
Here you go:
String str = "1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | | | | |".replaceAll("\\|", "").replaceAll("\\s+", "");
Do you mean like?
String s = "1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | | | | |";
for(String n : s.split(" ?\\| ?")) {
int i = Integer.parseInt(n);
System.out.println(i);
}
prints
1
2
3
4
5
6
7
inputString.split("\\s*\\|\\s*") will give you an array of the numbers as strings. Then you need to parse the numbers:
final List<Integer> ns = new ArrayList<>();
for (String n : input.split("\\s*\\|\\s*"))
ns.add(Integer.parseInt(n);
You can use split with the following regex (allows for extra spaces, tabs and empty buckets):
String input = "1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | | | | | ";
String[] numbers = input.split("([\\s*\\|\\s*])+");
System.out.println(Arrays.toString(numbers));
outputs:
[1, 2, 3, 4, 5, 6, 7]
Or with Java onboard methods:
String[] data="1 | 2 | 3 | 4 | 5 | 6 | 7 | | | | | | | |".split("|");
for(String elem:data){
elem=elem.trim();
if(elem.length()>0){
// do someting
}
}
Split the string at its delimiter | and then parse the array.
Something like this should do:
String test = "|1|2|3";
String delimiter = "|";
String[] testArray = test.split(delimiter);
List<Integer> values = new ArrayList<Integer>();
for (String string : testArray) {
int number = Integer.parseInt(string);
values.add(number);
}

Categories

Resources