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

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

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
==================================================================

Looping through 2d array with String.split()

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);

How to read Java files with contain many splits

BB [FL:60 BT:10 SG:20 MK:10 | 12]
I have above data on a text file and I need to get integer values separately
In brief first BB represent food type
with ":" split represents materials
and with "|" represent time to make it
How can i get those 5 parameters in java using File reader Thank you
Use a regex matcher here, and iterate over your input string, matching pure numbers as you go along:
String input = "BB [FL:60 BT:10 SG:20 MK:10 | 12]";
String regex = "\\d+(?=[^0-9.])";
List<Integer> vals = new ArrayList<>();
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
while (m.find()) {
vals.add(Integer.parseInt(m.group(0)));
}
for (int val : vals) {
System.out.println("Found an integer: " + val);
}
Found an integer: 60
Found an integer: 10
Found an integer: 20
Found an integer: 10
Found an integer: 12
Demo
Split, split, trim & split:
-> String code = "BB [FL:60 BT:10 SG:20 MK:10 | 12]"
| Added variable code of type String with initial value "BB [FL:60 BT:10 SG:20 MK:10 | 12]"
-> String[] mattime = code.split ("[\\]\\[\\|]");
| Modified variable mattime of type String[] with initial value [Ljava.lang.String;#2286778
| Update overwrote variable mattime
-> mattime [1]
| Expression value is: "FL:60 BT:10 SG:20 MK:10 "
| assigned to temporary variable $32 of type String
-> String[] elem = mattime [1].split (" ")
| Modified variable elem of type String[] with initial value [Ljava.lang.String;#13a5fe33
| Update overwrote variable elem
-> for (String e: elem) println (e);
FL:60
BT:10
SG:20
MK:10
-> for (String e: elem) {println (e); String [] kv = e.trim().split (":") ; println (kv[0] + " : " + Integer.parseInt (kv[1])); }
FL:60
FL : 60
BT:10
BT : 10
SG:20
SG : 20
MK:10
MK : 10

Write text to a file in Java

I'm trying to write a simple output to a file but I'm getting the wrong output. This is my code:
Map<Integer,List<Client>> hashMapClients = new HashMap<>();
hashMapClients = clients.stream().collect(Collectors.groupingBy(Client::getDay));
Map<Integer,List<Transaction>> hasMapTransactions = new HashMap<>();
hasMapTransactions = transactions.stream().collect(Collectors.groupingBy(Transaction::getDay));
//DAYS
String data;
for (Integer key: hashMapClients.keySet()) {
data = key + " | ";
for (int i = 0; i <hashMapClients.get(key).size();i++) {
data += hashMapClients.get(key).get(i).getType() + " | " + hashMapClients.get(key).get(i).getAmountOfClients() + ", ";
writer.println(data);
}
}
I get this output
1 | Individual | 0,
1 | Individual | 0, Corporation | 0,
2 | Individual | 0,
2 | Individual | 0, Corporation | 0,
But it should be, also it should not end with , if it's the last one.
1 | Individual | 0, Corporation | 0
2 | Individual | 0, Corporation
| 0
What am I doing wrong?
It sounds like you only want to write data to the output in the outer loop, not the inner loop. The latter of which is just for building the data value to write. Something like this:
String data;
for (Integer key: hashMapClients.keySet()) {
// initialize the value
data = key + " | ";
// build the value
for (int i = 0; i <hashMapClients.get(key).size();i++) {
data += hashMapClients.get(key).get(i).getType() + " | " + hashMapClients.get(key).get(i).getAmountOfClients() + ", ";
}
// write the value
writer.println(data);
}
Edit: Thanks for pointing out that the last character also still needs to be removed. Without more error checking, that could be as simple as:
data = data.substring(0, data.length() - 1);
You can add error checking as your logic requires, perhaps confirming that the last character is indeed a comma or confirming that the inner loop executes at least once, etc.
One problem is that you are calling println after every Client, rather than waiting until the whole list is built. Then, to fix the problem with the trailing comma, you can use a joining collector.
Map<Integer,List<Client>> clientsByDay = clients.stream()
.collect(Collectors.groupingBy(Client::getDay));
/* Iterate over key-value pairs */
for (Map.Entry<Integer, List<Client>> e : clientsByDay) {
/* Print the key */
writer.print(e.getKey());
/* Print a separator */
writer.print(" | ");
/* Print the value */
writer.println(e.getValue().stream()
/* Convert each Client to a String in the desired format */
.map(c -> c.getType() + " | " + c.getAmountOfClients())
/* Join the clients together in a comma-separated list */
.collect(Collectors.joining(", ")));
}

Getting data from cursor and store it in string array

ID| TOPIC | TITLE | TYPE | NAME |
---------------------------------
1 | AB | BCD | ref | Ferari|
----------------------------------
1 | AB | BCD | ref | TOYOTA|
----------------------------------
1 | AB | BCD | ref| AUDI |
----------------------------------
1 | AB | BCD | ref| BMW |
---------------------------------
2 | BC | ABC | ref | NISSAN|
----------------------------------
2 | BC | ABC | ref | SUZKI|
----------------------------------
2 | BC | ABC | ref| TATA |
Cursor hold data like this table. Now, I want get data like ID| TOPIC | TITLE | TYPE | NAME | here NAME can be multiple according to the ID. Like for ID 1 K will be FERARI,TOYOTA,AUDI,BMW and so on. I want to show this information in customlistview in a row .
My question is
is there any way to store the name data in String Array or do you have any alternative suggestion for me
If I understand your question correctly, you want to store your values from your database table in arrays? For that, you could create parallel resizeable generic lists like this:
ArrayList<int> ids = new ArrayList<int>();
ArrayList<String> topics = new ArrayList<String>();
ArrayList<String> titles = new ArrayList<String>();
ArrayList<String> types = new ArrayList<String>();
ArrayList<String> names = new ArrayList<String>();
And you can then add items to it like this:
ids.add(cursor.getInt(cursor.getColumnIndexOrThrow("_id")));
topics.add(cursor.getString(cursor.getColumnIndexOrThrow("TOPIC")));
titles.add(cursor.getString(cursor.getColumnIndexOrThrow("TITLE")));
types.add(cursor.getString(cursor.getColumnIndexOrThrow("TYPE")));
names.add(cursor.getString(cursor.getColumnIndexOrThrow("NAME")));
P.S. your database looks wrong - values in ID column should be unique, if ID is the primary key (it looks like it should be).
P.S.P.S
Another option would be to use Object-Oriented design - Create a class that has members like id, topic, title, type, name and so on.
public class Product {
private int id;
private String topic;
private String title;
private String type;
private String name;
public Product(Cursor cursor) {
this.id = cursor.getInt(cursor.getColumnIndexOrThrow("_id"));
this.topic = cursor.getString(cursor.getColumnIndexOrThrow("TOPIC"));
this.title = cursor.getString(cursor.getColumnIndexOrThrow("TITLE"));
this.type = cursor.getString(cursor.getColumnIndexOrThrow("TYPE"));
this.name = cursor.getString(cursor.getColumnIndexOrThrow("NAME"));
}
//Getter & Setter here...
}
this Product could be in a list of Products like:
ArrayList<Product> products = new ArrayList<Product>();
Product product = new Product(cursor);
products.add(product); // or simpler: products.add(new Product(cursor);
and you could use this list for many purposes like:
ArrayList<int> ids = new ArrayList<int>();
ArrayList<String> topics = new ArrayList<String>();
ArrayList<String> titles = new ArrayList<String>();
ArrayList<String> types = new ArrayList<String>();
ArrayList<String> names = new ArrayList<String>();
for (Product product : products) {
// for every product in your products list do:
ids.add(product.getId);
topics.add(product.getTopic);
titles.add(product.getTitle);
types.add(product.getType);
names.add(product.getName);
}

Categories

Resources