So basically someone gave me a text file for me to read through Java and I have to print out certain parts of it.
So what I did was I put all of the text file into a String and between every word there's a ":". So i split all of the text with ":" using split function. At first every line looks like this
firstName:Surname:Age:Country
firstName:Surname:Age:Country
firstName:Surname:Age:Country
firstName:Surname:Age:Country
firstName:Surname:Age:Country
firstName:Surname:Age:Country
firstName:Surname:Age:Country
firstName:Surname:Age:Country
After it would be the same thing without the colons.
So now if I say all[0], I would get all the firstNames only.
What I'm trying to get is get the top 3 highest ages but I do not know how to do that.
Explanation
Suppose you have a file with lines like
John:Doe:20:USA
Jane:Doe:35:Germany
Robert:Moe:14:Japan
Larry:Loe:25:China
Richard:Roe:27:India
and you want the 3 lines with the highest age, that would be
Jane:Doe:35:Germany
Richard:Roe:27:India
Larry:Loe:25:China
The procedure is straightforward. First read all lines, split by : and parse the data into a wrapper class like Person. Collect them into some collection like List<Person> and sort them using a Comparator that compares the age. Alternatively you could let Person implement Comparable and then use their natural order.
If efficiency matters you can also do partial sort since you are only interested in the top 3 hits. For this you could use a PriorityQueue, insert all elements and call poll three times.
Solution
First the Person wrapper class
public class Person {
private String mFirstName;
private String mSurname;
private int mAge;
private String mCountry;
public Person(String firstName, String surname, int age, String country) {
this.mFirstName = firstName;
this.mSurname = surname;
this.mAge = age;
this.mCountry = country;
}
// TODO Some getters
public String toString() {
return this.mFirstName + ":" + this.mSurname
+ ":" + this.mAge + ":" + this.mCountry;
}
public static Person parse(String[] data) {
String firstName = data[0];
String surname = data[1];
int age = Integer.parseInt(data[2]);
String country = data[3];
return new Person(firstName, surname, age, country);
}
}
Next we read all lines, split the data and parse them into Person. After that we sort and limit the result to 3. Finally we collect to a List and print the results.
Path file = Paths.get(...);
Pattern separator = Pattern.compile(":");
List<Person> persons = Files.lines(file) // Stream<String>
.map(separator::splitAsStream) // Stream<String[]>
.map(Person::parse) // Stream<Person>
.sorted(Comparator.comparingInt(Person::getAge).reversed())
.limit(3)
.collect(Collectors.toList());
persons.forEach(System.out::println);
Or if you want to use the PriorityQueue as suggested, which will improve runtime:
Path file = Paths.get(...);
Pattern separator = Pattern.compile(":");
PriorityQueue<Person> personQueue = Files.lines(file)
.map(separator::splitAsStream)
.map(Person::parse)
.collect(Collectors.toCollection(() -> {
return new PriorityQueue<>(
Comparator.comparingInt(Person::getAge).reversed());
}));
List<Person> persons = new ArrayList<>(3);
persons.add(personQueue.poll());
persons.add(personQueue.poll());
persons.add(personQueue.poll());
persons.forEach(System.out::println);
Related
I made a method that search through an array list of objects. Then if the searchKey is found in the array list it will print this certain item.
Here is how I iterate through the array list if it contains the searchKey, but I just realized that it is impossible to compare a string and an object.
for(int x = 0; x < Student.students.size(); x ++){
if(Student.students.contains(searchKey))
System.out.print(Student.students.get(x));
}
Here's how I create the constructor and array list.
String firstName, lastName, course, yearLevel, gender;
Student(String firstName, String lastName, String course, String yearLevel, String gender)
{
this.firstName = firstName;
this.lastName = lastName;
this.course = course;
this.yearLevel = yearLevel;
this.gender = gender;
}
static ArrayList<Student> students = new ArrayList<Student>();
You need to compare the one property; also you can use a for-each loop to simplify the code
for(Student s : Student.students){
if(s.getName().equals(searchKey))
System.out.print(s);
}
Note :
When you use a condition Student.students.contains(searchKey) in a loop, and it doesn't use the iteration variable that means there is a problem
You haven't defined what 'contains' means here but I'm going to assume that it means a Student contains the key if it appears as a substring in any of its String members. So let's start with a method that does that. We can define this as part of the Student class itself.
public class Student {
.... other stuff ....
/**
* Return true if any of the properties of this Student
* contain the given substring, false otherwise
*/
public boolean contains(String s) {
// consider addressing null cases - omitting for simplicity
return firstName.contains(s) ||
lastName.contains(s) ||
course.contains(s) ||
yearLevel.contains(s) ||
gender.contains(s);
}
}
Now you can iterate over your List and invoke this method to find the matches. Note that you need to handle the case that multiple Students may match a given search key (or none may match). So I would suggest collecting the results in a separate List. One does not generally iterate over Lists via the index. This example uses an enhanced for-loop (aka for-each).
public List<Student> findMatches(List<Student> students, String key) {
List<Student> found = new ArrayList<>();
for (Student s : students) {
if (s.contains(key)) {
found.add(s);
}
}
return found;
}
This is a good case for using the Stream API.
public List<Student> findMatches(List<Student> students, String key) {
return students.stream()
.filter(s -> s.contains(key))
.collect(Collectors.toList());
}
You already know comparing Object to String makes no sense. So, most probably what you are trying to do is check if any of the attributes of your Student object(name/course/year etc) has a value that matches your search key. To do that you need to convert your object into a String.
Add a toString method to your Student class which will look something like this:
public String toString() {
return "Student [firstName=" + firstName + ", lastName=" + lastName + ", course=" + course + ", yearLevel="
+ yearLevel + ", gender=" + gender + "]";
}
Then, look for your searchKey in the string representation of your objects while iterating.
for(int x = 0; x < students.size(); x ++){
if(students.get(x).toString().contains(searchKey))
System.out.print(students.get(x).toString());
}
Edit: As rightly pointed out by Jon Skeet in the comments, the default toString method will generate incorrect results, a custom implementation should be used to convert the object to String.
I am working on a group generator and currently I am making an ArrayList from this txt file.
So that, the ArrayList is in the form of [PedroA, Brazil, Male, 10G, Saadia...]
I want to shuffle 4 elements at a time, to randomize this arraylist.
I am storing the info in
ArrayList<String> studentInfo = info.readEachWord(className);
This is very hard to do. It's possible, of course, but difficult.
It is being made difficult because what you want to do is bizarre.
The normal way to do this would be to:
Make a class representing a single entry, let's call it class Person.
Read this data by parsing each line into a single Person instance, and add them all to a list.
Just call Collections.shuffle(list); to shuffle them.
If we have the above, we could do what you want, by then converting your List<Person> back into a List<String>. In many ways this is the simplest way to do the task you ask for, but then you start wondering why you want this data in the form of a list of strings in the first place.
enum Gender {
MALE, FEMALE, OTHER;
public static Gender parse(String in) {
switch (in.toLowerCase()) {
case "male": return MALE;
case "female": return FEMALE;
default: return OTHER;
}
}
class Person {
String name;
String location;
Gender gender;
[some type that properly represents whatever 10G and 10W means];
public static Person readLine(String line) {
String[] parts = line.split("\\s+", 4);
Person p = new Person();
p.name = parts[0];
p.location = parts[1];
p.gender = Gender.parse(parts[2]);
...;
return p;
}
}
you get the idea.
I need to get user input to either create lists, tuples, hashtables, etc. for ordered or key keeping. For example in python it would look like this when completed:
all = {'main1':{'name':'Hannag', 'birth':'date1', 'gender':'female'}, 'main2':{'name':'Kate', 'birth':'date2', 'gender':'female'}, 'main3':{'name':'Ben', 'birth':'date3', 'gender':'male'}}
In java I haven't figured out how to create a string key with more string keys inside of it. main1':{'name':'Hannag', 'birth':'date1', 'gender':'female'}
I've tried using tuples, but you can only do a set number from I what I've tried using tuple3.
Map<String, Tuple3<String, String, String>> dos = new HashMap<String, Tuple3<String, String, String>>();
while(adds.contains(contin))
{
String li = input.nextLine();
String fName = input.nextLine();
String fDir = input.nextLine();
do.Add(fName, new Tuple3((String) li, (String) fName, (String) fDir));
I also tried using hashtable inside hashtable, but hashtable titles can't be strings. I need the names of hashtables or lists etc to be strings. Is there any way I could pull this off?
Instead of messing around with tuples and maps, create a class:
class Person {
final String name;
final String birth; // maybe make this a Date?
final String gender; // if you subscribe to the gender binary, maybe make this an enum?
Person(String name, String birth, String gender) { // constructor
this.name = name;
this.birth = birth;
this.gender = gender;
}
}
and then have a
Map<String, Person> map = new HashMap<>();
map.put("main1", new Person("Hannag", "date1", "female"));
...
Just wondering if you can use a splitter class to split up details moved from a LinkedList to and iterated one? This is the code of the initial split I used before I iterated the LinkedList:
Scanner input = new Scanner(new File("today.txt"));
while (input.hasNextLine())
{
names = input.nextLine();
if(names.contains(":"))
{
splitter2 = names.split(":");
name = splitter2[0];
times = splitter2[1];
System.out.printf("%s\t\t %s \n",name, times);
}
q1.add(names);
}
Q1 being the LinkedList that i have created.
Is there anyway to split the iterated list so that i can only search for name when calling back the new Iterated List?
If I understand you correctly, a Map would suit your needs better than a LinkedList.
You can have a Map<String,String> where the key is name and the value is times. Or you can have a Map<String,SomeObject> where the key is the name and the value some object that contains the data you read from the line.
Then, instead of q1.add(names), you can have :
Map<String,String> map = new HashMap<>();
...
while (input.hasNextLine()) {
...
map.put (name,times);
...
}
or
Map<String,SomeObject> map = new HashMap<>();
...
while (input.hasNextLine()) {
...
map.put (name,new SomeObject(name,times);
...
}
Later you can search the map for a specific name (map.containsKey(name) or map.get(name)), or iterate over all the names (using map.keySet()).
This is a little unclear, but I think what you're looking for is a HashMap. If the idea is to put the names and times into a data structure so that you can later search by name, then you want a HashMap<String,String> map, and then
map.put(name,times);
to add to the map. Later on, you can retrieve the times for a particular name with
map.get(name);
There are some assumptions here:
You don't care about the order of the names (see LinkedHashMap if you do care).
The names are unique (see Guava's Multimap if they're not unique).
You can create a custom Class, change q1 to a list of this type, add the elements if they can be split.
final List<Person> q1=new LinkedList<Person>();
{...other code...}
//your code change to add to this list when split can occur
if(names.contains(":"))
{
splitter2 = names.split(":");
name = splitter2[0];
times = splitter2[1];
System.out.printf("%s\t\t %s \n",name, times);
q1.add(new Person(name,times);
}
Then you can iterate the list and compare the attribute name with a search key:
final String searchKey="george";
for(final Person person : q1){
if(person.getName().equalsIgnoreCase(searchKey))
System.out.println("I found " + searchKey +"!");
}
Person class:
public class Person {
private String name;
private String time;
public Person(String name, String time) {
this.name = name;
this.time = time;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setTime(String time) {
this.time = time;
}
public String getTime() {
return time;
}
}
Alternatively, you can iterate and split the String only list, during the iteration, in the same way you did before. The previous method is better.
Example:
public static void main(String[] args) {
final String search = "george";
List<String> q1 = new LinkedList<String>();
q1.add("tom:120000");
q1.add("george:130000");
q1.add("john:120000");
for (final String q : q1) { //for each string q in the list q1
if (q.contains(":")) {
final String[] split = q.split(":");
final String name = split[0];
final String time = split[1];
if (name.equalsIgnoreCase(search)) {
System.out.println("I found " + search + "!");
System.out.println(name + " : " + time);
break;
}
}
}
}
I have a file of information separated by commas of which I need to tokenize and place into arrays.
The file has info such as
14299,Lott,Lida,22 Dishonest Dr,Lowtown,2605
14300,Ryder,Joy,19 Happy Pl,Happyville,2701
and so forth. I need to tekonize those pieces of information of which is separated by a comma. I'm not sure how to write out the tokenizer code to make it separate. I've managed to count the amount of lines in the document with;
File customerFile = new File("Customers.txt");
Scanner customerInput = new Scanner(customerFile);
//Checks if the file exists, if not, the program is closed
if(!customerFile.exists()) {
System.out.println("The Customers file doesn't exist.");
System.exit(0);
}
//Counts the number of lines in the Customers.txt file
while (customerInput.hasNextLine()) {
count++;
customerInput.nextLine();
}
And I also have the class of which I'll be placing the tokenized info into;
public class Customer {
private int customerID;
private String surname;
private String firstname;
private String address;
private String suburb;
private int postcode;
public void CustomerInfo(int cID, String lname, String fname, String add, String sub, int PC) {
customerID = cID;
surname = lname;
firstname = fname;
address = add;
suburb = sub;
postcode = PC;
}
But after this point I'm not sure how to place the info into the arrays of the customer. I've tried this but it's not right;
for(i = 0; i < count; i++) {
Customer cus[i] = new Customer;
}
It's telling me the 'i' and the new Customer are errors as it 'can't convert Customer to Customer[]' and 'i' has an error in the token.
first, you need to declare the Customer Array:
Customer[] cus = new Customer[count];
Now, the programm knows, how much space it has to allocate on the memory.
Then, you can use your loop, but you have to call the constructor of the class Customer and give him all the information it needs to create a new one :
for(i = 0; i < count; i++) {
Customer cus[i] = new Customer(cID, lname, fname, add, sub, PC);
}
Another thing you would be asking yourself about is, how do i get the data from the Strings/lines into the array.
for that, you should write all lines in an ArrayList. Like this.
ArrayList<String> strList = new ArrayList<String>();
while (customerInput.hasNextLine()) {
count++;
strList.add(customerInput.nextLine());
}
Now you got all lines as Strings in an ArrayList. But you want to give the single values of each String to your constructor.
take a look at the split method from Strings. (How to split a string in Java).
With split() you can split one line like this:
String[] strArray = "word1,word2,word3".split(",");
then in the strArray you can find your data:
strArray[0] would have the value "word1";
strArray[1] = "word2";
and so on
If it is a CSV file instead of a simple comma separated flie, maybe consider some library like:
Commons CSV
OpenCSV