How to stop string repetition on array - java

We I have this college assignment where I have to Read a file with a list of names and add up to 3 presents to each one. I can do it but the presents are repeating and some people in the list are getting the same present more than once. How can I stop it so each person receives different variety of present each time?
Here is my code:
public static void main(String[] args) throws IOException {
String path = "Christmas.txt";
String line = "";
ArrayList<String> kids = new ArrayList<>();
FileWriter fw = new FileWriter("Deliveries.txt");
SantasFactory sf = new SantasFactory();
try (Scanner s = new Scanner(new FileReader("Christmas.txt"))) {
while (s.hasNext()) {
kids.add(s.nextLine());
}
}
for (String boys : kids) {
ArrayList<String> btoys = new ArrayList<>();
int x = 0;
while (x < 3) {
if (!btoys.contains(sf.getRandomBoyToy().equals(sf.getRandomBoyToy()))) {
btoys.add(sf.getRandomBoyToy());
x++;
}
}
if (boys.endsWith("M")) {
fw.write(boys + " (" + btoys + ")\n\n");
}
}
fw.close();
}
}

Just use a Set data structure instead of a List.

if (!btoys.contains(sf.getRandomBoyToy().equals(sf.getRandomBoyToy()))) {
btoys.add(sf.getRandomBoyToy());
x++;
}
Generates 3 toys, comparing 2 of them with each other first, and checking if the resulting boolean is present in the list of strings (which it presumably isn't), then appending the 3rd one.
Instead you should generate a single one, and use it for both checking and adding:
String toy = sf.getRandomBoyToy();
if(!btoys.contains(toy)) {
btoys.add(toy);
x++;
}

The set interface present in the java.util package and extends the Collection interface is an unordered collection of objects in which duplicate values cannot be stored. It is an interface which implements the mathematical set. This interface contains the methods inherited from the Collection interface and adds a feature which restricts the insertion of the duplicate elements. There are two interfaces which extend the set implementation namely
for (String boys : kids) {
Set<String> btoys = new HashSet<String>();
btoys.add(sf.getRandomBoyToy());
if (boys.endsWith("M")) {
fw.write(boys + " (" + btoys + ")\n\n");
}
}

Related

If matched then add elements of ArrayList_A to ArrayList , if not then add elements of ArrayList_B to ArrayList

I have the following code:
package sportsCardsTracker;
import java.io.*;
import java.text.*;
import java.util.*;
import java.util.stream.Collectors;
public class Test_Mark6 {
public static ArrayList<String> listingNameList;
public static ArrayList<String> finalNamesList;
public static void main(String[] args) throws IOException, ParseException {
listingNameList = new ArrayList();
listingNameList.add("LeBron James 2017-18 Hoops Card");
listingNameList.add("Stephen Curry Auto Patch, HOT INVESTMENTS!");
listingNameList.add("Michael Jordan 1998 Jersey Worn Card");
ArrayList<String> playersNamesList = new ArrayList();
playersNamesList.add("LeBron James");
playersNamesList.add("Stephen Curry");
playersNamesList.add("Michael Jordan");
finalNamesList = new ArrayList();
String directory = System.getProperty("user.dir");
File file = new File(directory + "/src/sportsCardsTracker/CardPrices.csv");
FileWriter fw = new FileWriter(file, false); //true to not over ride
for (int i = 0; i < listingNameList.size(); i++) {
for (String listingNames : listingNameList) {
List<String> result = NBARostersScraper_Mark3.getNBARoster().stream().map(String::toLowerCase).collect(Collectors.toList());
boolean valueContained = result.stream().anyMatch(s -> listingNames.toLowerCase().matches(".*" + s + ".*"));
if(valueContained == true) {
finalNamesList.add(//The players' name);
}
}
fw.write(String.format("%s, %s\n", finalNamesList.get(i)));
}
}
}
Basically, in the listingsNameList, I have the listing's names and in the playersNamesList, I have all the players' names. What I would like is that, if the code matches the names between the two arrayList and find a player's name, it should returns the players' only.
For example, instead of "LeBron James 2017-18 Hoops Card" it should return "Lebron James" only. If it does not find anything, then just return the listing's name. So far, I have created a new ArrayList namely finalNamesList, my idea would be using an if statement (if match found then add players' name to finalNamesList, if not add the listing' name to finalNamesList). However the code above is not working and it is just adding all of the names in the listingNameList to the finalNamesList. I suspect that the way I grab the index is wrong - but I don't know how to fix it.
The method you are using to match a pattern that seems wrong. Instead of "match()" you can use string contains method as below.
List<String> temp = new ArrayList<>();
for (String listingNames : listingNameList) {
temp = playersNamesList.parallelStream().filter(s -> listingNames.toLowerCase().contains(s.toLowerCase())).map(s -> s).collect(Collectors.toList());
if(temp.size() > 0){
System.out.println(temp.get(0));
//fw.write(String.format("%s, %s\n", temp.get(0));
}
}
One more thing, You don't need to use 2 for loop here, with one loop you can achieve your output.
Though You can still optimize this code, I have taken the temp list above that you can avoid.

How to update an element in an array?

How can I get the Observation method to increase a single element in the numberofObs array by 1, every time I use it?
I get this error: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 0
public class Database {
private ArrayList <String> databaseOfBirds = new ArrayList<String>();
private int[] numberofObs = new int[databaseOfBirds.size()];
public void add(String name, String latinName) {
databaseOfBirds.add(name + "(" + latinName + ")");
}
public String Observation(String birdname){
for(int i = 0; i < databaseOfBirds.size(); i++) {
if(databaseOfBirds.get(i).contains(birdname)) {
numberofObs[i]++;
return "";
}
}
return "Is not a bird!";
}
}
When you create your numberofObs array it's length is zero, since at that point databaseOfBirds is empty. Unless you're growing the array in some unlisted code you'll get a NullPointerException the first time you find a bird.
You could fix this by making numberofObs a List:
private List<Integer> numberofObs = new ArrayList<>();
Then in your add method add the line:
numberofObs.add(0);
Finally, change
numberofObs[i]++;
to
numberofObs.set(i, 1 + numberofObs.get(i));
Implementation is wrong in 2 places:
numberOfObs assignment will trigger ArrayIndexOutOfBoundsException since you are not resizing it each time a new bird is added to database
you are not increasing anything on the matching bird counter
I am understanding you want to have a separate counter for each bird matched by Observation. Here you are (there are several approaches in terms of data structures, taking the one easiest to understand):
public class Database {
private List<String> databaseOfBirds = new ArrayList<>();
private List<Integer> numberofObs = new ArrayLidt<>();
public void add(String name, String latinName) {
databaseOfBirds.add(name + "(" + latinName + ")");
numberofObs.add(0);
}
public String Observation(String birdname){
for(int i = 0; i < databaseOfBirds.size(); i++) {
if (databaseOfBirds.get(i).contains(birdname)) {
numberofObs.set(i, numberofObs.get(i)+1);
System.out.println(numberofObs.get(i));
return "";
}
}
return "Is not a bird!";
}
}
I suggest using a Map instead of 2 Lists.
private Map<String, Long> birds = new HashMap<>();
Easier to manage. Unless memory is scarce, you probably don't want to use int[].

creating new ArrayList with elements from another one

I'm writing the code of my first Java game right now and I have a problem with ArrayList. I have one, with elements like nicknames and scores, to be specific I create it from the text file(code below):
static ArrayList<String> results = new ArrayList<String>();
BufferedReader br = new BufferedReader(new FileReader(ranking));
String line = br.readLine();
results.add(line);
while(line != null) {
line = br.readLine();
results.add(line);
}
br.close();
So the file looks like this:
nick1
score1
nick2
score2
...
I would like to make a ranking with top 10 best results, my idea is to make the class with the fields nick and score and somehow assign that fields to appriopriate ones from the ArrayList. Maybe I can do this like this:(?)
for(int i = 0;i<results.size();i=i+2){
nick = results.get(i);
}
for(int i = 1;i<results.size();i=i+2){
score = results.get(i);
}
Then I would create a new ArrayList, which would be in the type of that new class. But my problem is that I don't exactly know how I can connect values from 'old' ArrayList with the paramaters of future type of new ArrayList. The new one should be like:
static ArrayList<Ranking> resultsAfterModification = new ArrayList<Ranking>();
Ranking rank = new Ranking(nick, score);
Then I can easily compare players' scores and make a solid ranking.
You can create a class Player that contains the name and score of each player. The Player class should implement the Comparable interface which is Java's way of figuring out the logical order of elements in a collection:
public class Player implements Comparable<Player>
{
private String _name;
private double _score;
public Player(String name, double score)
{
this._name = name;
this._score = score;
}
public String getName()
{
return this._name;
}
public double getScore()
{
return this._score;
}
// Since you probably want to sort the players in
// descending order, I'm comparing otherScore to this._score.
#Override
public int compareTo(Player otherScore)
{
return Double.compare(otherScore._score, this._score);
}
#Override
public String toString()
{
return "Name: " + this._name + ", Score: " + Double.toString(this._score);
}
}
Once you've created the Player class, you can read both name and score in one go and use the Collections utility class to sort the Player list. Last but not least, you could grab the top ten by using the subList method: (this assumes that the file will have a score for each name and the file will be in the format you specified above)
public static void main(String[] args)
{
List<Player> results = new ArrayList<Player>();
BufferedReader br;
try
{
br = new BufferedReader(new FileReader("myFile.txt"));
String line = br.readLine();
while(line != null)
{
String name = line;
double score = Double.parseDouble(br.readLine());
results.add(new Player(name, score));
line = br.readLine();
}
br.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
// Sort using the java.util.Collections utility class
// Sorting will modify your original list so make a copy
// if you need to keep it as is.
Collections.sort(results);
// Top 10
List<Player> top10 = results.subList(0, 10);
}
1) You should program by Interface (List instead of ArrayList in declared type) here because you don't use methods specific to ArrayList.
List<Ranking> resultsAfterModification = new ArrayList<Ranking>();
2)
Then I would create a new ArrayList, which would be in the type of
that new class. But my problem is that I don't exactly know how I can
connect values from 'old' ArrayList with the paramaters of future type
of new ArrayList.
To do it, you don't need many changes.
Your idea a is little too complex because finally you perform two mappings : one where you store from read line to List of String and another one where you store from List of String to List of Ranking .
You can direct map read line to List of Ranking.
List<Ranking> rankings = new ArrayList<Ranking>();
BufferedReader br = new BufferedReader(new FileReader(ranking));
String nick = null;
while ( (nick = br.readLine()) != null){
String score = br.readLine();
rankings.add(new Ranking(nick, score));
}
You can save array in file :
1 use, FileInputStram and FileOuputStream to write and read array object to file.
2 use Gson library to save and load array as json
3 write as normal textfile like below:
Player 1
Score 1
Player 2
Score 2
....
ArrayList<Player> list=new ArrayList();
Scanner sc=new Scanner(new File("filepath.txt"));
While(sc.hasNextLine()){
Player p=new Player();
p.name=sc.nextLine();
If(sc.hasNextFloat()){
p.score=sc.nextFloat();
list.add(p);
}
}
Arrays.sort(list,...);
And can sort array by Arrays.sort()
So your Ranking class might look like
public class Ranking {
public String nick;
public int score;
}
I think the nick and score are two ArrayList containing nicks and scores respectively.
So to create a common ArrayList, if the sizes of nick and score are the same, you might do something like this.
static ArrayList<Ranking> resultsAfterModification = new ArrayList<Ranking>();
for(int i = 0; i < nick.size(); i++) {
Ranking rank = new Ranking(nick.get(i), score.get(i));
resultsAfterModification.add(rank);
}
Now you need to write your own comparator to sort the values inside resultsAfterModification
Collections.sort(resultsAfterModification, new Comparator<Ranking>() {
#Override
public int compare(Ranking r1, Ranking r2) {
if (r1.score > r2.score)
return 1;
if (r1.score < r2.score)
return -1;
return 0;
}
});

Why am I unable to assign an embedded ArrayList<Integer> to a locally declared ArrayList<Integer>?

Sorry if the title is not clear, I'm not very good with programming jargon.
I have 2 string ArrayLists and an integer ArrayList obtained from one method which is passed to a separate method through the collection LinkedHashMap< String, List< String>>. However, when I try to set the integer ArrayList into a empty ArrayList declared in the receiving method, it shows the syntax error: "incompatible types: List< String> cannot be converted to List< Integer>".
Starter Method:
public static void main(String[] args) {
try{
LinkedHashMap lhm = new LinkedHashMap();
List<String> listEPC = new ArrayList<String>();
List<String> listTimeStamp = new ArrayList<String>();
List<Integer> listAntenna = new ArrayList<Integer>();
String tagID = "EQ5237";
String TimeStampStr = "12:23:22";
int tagAntenna = 2;
listEPC.add(tagID);
listTimeStamp.add(TimeStampStr);
listAntenna.add(tagAntenna);
lhm.put("epcs", listEPC);
lhm.put("timestamps", listTimeStamp);
lhm.put("antennas", listAntenna);
insertData insert = new insertData();
insert.insertData(lhm); //send map with values to new method
}catch(Exception e){
e.printStackTrace();
}
}
Receiving Method:
public class insertData {
public void insertData(LinkedHashMap<String, List<String>> readMap) {
List<String> listEPC = new ArrayList<String>();
List<String> listTimeStamp = new ArrayList<String>();
List<Integer> listAntenna = new ArrayList<Integer>();
String EPC = null;
String TimeStamp = null;
Integer Antenna = null;
listEPC = readMap.get("epcs");
listTimeStamp = readMap.get("timestamps");
listAntenna = readMap.get("antennas"); //error message here
for(int i=0; i<readMap.size(); i++){
EPC = listEPC.get(i);
TimeStamp = listTimeStamp.get(i);
Antenna = listAntenna.get(i);
System.out.println("Entry " + i );
System.out.println("Values: " + EPC + TimeStamp + Antenna);
}
}
}
This code works only if I change all instances of integers to strings, which is not what I would like in my actual code. Why is it so and how do I work around it?
You can't assign a List<String> to a List<Integer>. The elements are fundamentally different types.
You would need to construct a new List:
List<Integer> listOfIntegers = new ArrayList<>();
for (String entry : listOfStrings) {
listOfIntegers.add(Integer.valueOf(entry);
}
Of course, you also need to handle the possibility that elements of the list cannot be parsed as integers.
However, you are just throwing away type information by stuffing everything into a single map. It would be better to pass the three lists separately:
insertData(listEPC, listTimestamp, listAntenna);
and then you can have different list types in the method signature:
void insertData(
List<String> listEPC,
List<String> listTimestamp,
List<Integer> listAntenna) { ... }
I am going to include the proper answer at the bottom, but in regards to your question title, you'll have to change your method signature to:
LinkedHashmap<String, List<?>> readMap;
Then either cast the lists, which will cause an unsafe cast. eg.
List<String> listEPC = (List<String>)readMap.get("epcs");
Or cast the object.
List<?> listEPC = readMap.get("epcs");
Then in the loop cast.
EPC = (String)listEPC.get(i);
Note, these are not good solutions.
What you should have is one List that contains an object with all of the data's you need.
I can imagine the thought process went something along these lines, "I have these things, and they contain two strings and an integer. I will create a variable for each." Then you ask the question, "How do I create a collection of these things?"
The wrong answer to this question is, "I will make a list for each value, and match associated values by index." The correct answer is, "I will create a class to represent my data, and store that in a list." This is the basic essence of object orient programming (welcome to java).
First we design the class:
class EPCThing{
String EPC;
String timeStamp;
int Antennas;
public EPCThing(String tagId, String timeStamp, int antennas){
EPC=tagId;
this.timeStamp = timeStamp;
Antennas = antennas;
}
#Override
public String toString(){
return "Values: " + EPC + TimeStamp + Antenna
}
}
Now your program's main method will be something like.
List<EPCThing> things = new ArrayList<>();
String tagID = "EQ5237";
String TimeStampStr = "12:23:22";
int tagAntenna = 2;
EPCThing thing = new EPCThing(tagID, TimeStampStr, tagAntenna);
things.add(thing);
insertData insert = new insertData();
insert.insertData(things);
Then we can fix your insertData method
public void insertData(List<EPCThing> things) {
for(int i=0; i<things.size(); i++){
System.out.println("Entry " + i );
System.out.println("Values: " + things.get(i));
}
}

Trying to remove duplicate elements

I'm messing around trying to learn to use HashSets to remove duplicate elements in my output but I'm running into some trouble.
My goal is to select a text file when the program is run and for it to display the words of the text file without duplicates, punctuation, or capital letters. All of it works fine except for removing the duplicates.
This is my first time using a Set like this. Any suggestions as to what I'm missing? Thanks!
Partial text file input for example: "Four score and seven years ago our fathers brought forth on this continent a new nation, conceived in liberty, and dedicated to the proposition that all men are created equal. Now we are engaged in a great civil war, testing whether that nation, or any nation, so conceived and so dedicated, can long endure"
import java.util.Scanner;
import java.util.List;
import java.io.*;
import java.util.*;
import javax.swing.JFileChooser;
public class Lab7 {
public interface OrderedList<T extends Comparable<T>> extends Iterable<T>
{
public void add(T element);
public T removeFront();
public T removeRear();
public int size();
public boolean isEmpty();
public boolean contains(T element);
public Iterator<T> iterator();
}
public static void main(String[] arg) throws FileNotFoundException
{
Scanner scan = null;
JFileChooser chooser = new JFileChooser("../Text");
int returnValue = chooser.showOpenDialog(null);
if( returnValue == JFileChooser.APPROVE_OPTION)
{
File file = chooser.getSelectedFile();
scan = new Scanner(file);
}
else
return;
int count = 0;
Set<String> set = new LinkedHashSet<String>();
while(scan.hasNext())
{
String[] noDuplicate = {scan.next().replaceAll("[\\W]", "").toLowerCase()};
List<String> list = Arrays.asList(noDuplicate);
set.addAll(list);
count++;
}
scan.close();
System.out.println(set);
System.out.println();
System.out.println(chooser.getName() + " has " + count + " words.");
}
}
Your problem is that you are creating a new HashSet every time you read a word using the Scanner, so there is no chance for it to do de-duplication. You can fix it with the following steps. Also, normal HashSet does not retain ordering.
Create the HashSet once, before the Scanner loop.
Use a LinkedHashSet, so that order is preserved in the same order that you added it.
Inside the loop, use set.add(item);. As the other answer mentions, you do not need to create a one-element list.
Adding the code for completeness.
public static void main(String[] arg) throws FileNotFoundException
{
Scanner scan = null;
scan = new Scanner(new File("Input.txt"));
int count = 0;
Set<String> set = new LinkedHashSet<String>();
while(scan.hasNext())
{
String word = scan.next().replaceAll("[\\W]", "").toLowerCase();
set.add(word);
count++;
}
scan.close();
// System.out.println(set);
System.out.println();
System.out.println("Input.txt has " + count + " words.");
// How do I print a set by myself?
for (String word : set) {
// Also remove commas
System.out.println(word.replaceAll(",",""));
}
}
I would do it this way:
Set<String> set = new LinkedHashSet<String>();
while(scan.hasNext())
{
String noDuplicate = scan.next().replaceAll("[\\W]", "").toLowerCase();
set.add(noDuplicate);
}
scan.close();
System.out.println("The text has " + set.size() + " unique words.");
Your solution (Creating a one element array, converting that to a List, and converting that to a HashSet) is extremely inefficient, in addition to being incorrect. Just use the String you're originally working with, and add it to the LinkedHashSet (which will preserve ordering). At the end set.size() will show you the number of unique words in your sentence.

Categories