I am trying to write a program where the user can write their grocery item list, and then they can search whether they have wrote that item on the list or not. When they find if they have wrote down the item name it's suppose to return the index number and if they haven't wrote down the item name it's suppose to return -1. So far my code is able to find out if they have wrote down the item or not in Boolean expression.
import java.util.Scanner;
import java.util.Arrays;
public class GroceryStoreTest {
// for the search item we need to create a method.
I wrote down a boolean method for the search item.
public static boolean contains(String [] arr, String targetItem){
for(String find : arr){
if(targetItem.equals(find))
return true;
} return false ;
}
public static void main(String[] args) {
//lets create an array of string and add items list
int list;
Scanner input = new Scanner(System.in);
System.out.print("How many items would you like to add to the list: ");
list = input.nextInt();
String[] items = new String[list];
System.out.print("Write down the name of item without any space.\n(i.e.; Green Paper = GreenPaper)\n\nItems list: \n");
int i;
for (i = 0; i < list; i++) {
System.out.printf((i+1 ) + ".");
items[i] = input.next();// asks for next item name
}
// search in the items list
String search = " ";
System.out.print("Enter the name of the item to check if you have listed: " + search);
search = input.next();
//System.out.print(contains(items, String.valueOf(input)));
System.out.print(contains(items, search));
input.close();// it will stop taking the input
}
}
The other answers are correct, but there is another perspective: you should understand the difference between the for-each and the "counting-for" loops.
You got:
public static boolean contains(String [] arr, String targetItem){
for(String find : arr){
if(targetItem.equals(find))
return true;
}
return false ;
}
Which is already pretty good. ( Maybe expect for the subtle thing that I would advise you to always put blocks in { braces }; as it is far too easy to get things wrong in order to save that little bit of typing. )
But in general, that code is OK. One should actually prefer this style of looping. Because that construct really improves readability by making you "read less".
But in your case: you need that index. So the for-each loop doesn't give you what you need. Thus - this is a case where an old school counter loop is better:
for (int i=0; i<arr.length;i++) {
... and now i is the index you are looking for; thus the value to return
or -1 in the end if nothing matched
Why not just change your contains method to return an int instead?
public static int indexOf(String [] arr, String targetItem){
for(int i = 0 ; i < arr.length ; i++) {
if(targetItem.equals(arr[i]))
return i;
}
return -1 ;
}
Alternatively, you don't need to create your own indexOf method, you can just call Arrays.asList(items).indexOf("valueSearch") to get the index.
Another way to do this is to use an array list instead of an array. An ArrayList is basically an array without a fixed length. Your code will look like this:
Scanner input = new Scanner(System.in);
ArrayList<String> items = new ArrayList<>();
System.out.print("Write down the name of item without any space.\n(i.e.; Green Paper = GreenPaper)\n\nWrite "END" to finish entering items\n\nItems list: \n");
while(true) {
String item = input.next();
if (item.equals("END")) {
break;
}
items.add(item);
}
// search in the items list
String search = " ";
System.out.print("Enter the name of the item to check if you have listed: " + search);
search = input.next();
System.out.print(items.indexOf(search));
input.close();// it will stop taking the input
Here is an example of how to remove all occurences of something from an array list:
ArrayList<String> a = new ArrayList<>();
a.add("Hello");
a.add("Hello");
while (a.contains("Hello")) {
a.remove("Hello");
}
through a cycle, comparing the values with the value adding looking toLoweCase method to avoid problems with case sensitive
for(int i = 0 ; i < items.length ; i++) {
if(targetItem.toLowerCase().equals(items[i].toLowerCase()))
System.out.println("Index " + i);
}
or using the ArrayList class can be done as follows
System.out.println(Arrays.asList(items).indexOf("valueSearch"));
Related
I have a list of words that have both 'simple' and 'compound' words in them, and would like to implement an algorithm that prints out a list of words without the compound words that are made up of the simple words.
Sampel input:
chat, ever, snapchat, snap, salesperson, per, person, sales, son, whatsoever, what, so
Desired output:
chat, ever, snap, per, sales, son, what, so
I have written the following, but am stuck as to how to take it on from here:
private static String[] find(String[] words) {
ArrayList<String> alist = new ArrayList<String>();
Set<String> r1 = new HashSet<String>();
for(String s: words){
alist.add(s);
}
Collections.sort(alist,new Comparator<String>() {
public int compare(String o1, String o2) {
return o1.length()-o2.length();
}
});
int count= 0;
for(int i=0;i<alist.size();i++){
String check = alist.get(i);
r1.add(check);
for(int j=i+1;j<alist.size();j++){
String temp = alist.get(j);
//System.out.println(check+" "+temp);
if(temp.contains(check) ){
alist.remove(temp);
}
}
}
System.out.println(r1.toString());
String res[] = new String[r1.size()];
for(String i:words){
if(r1.contains(i)){
res[count++] = i;
}
}
return res;
}
Any guidance/insight or suggestions to a better approach would be appreciated.
I tried to go through your code, looks like "son" is not in your output. I believe it failed because of this line:
if(temp.contains(check)) { <-- wrong check.
alist.remove(temp);
}
So instead of simply checking if temp.contains(check), you should have a small loop that does the following:
does temp start with check?
if 1) passed, then let temp = temp.substring(check.length), then go back to 1) again, until temp == "";
Another implementation would be setting up a trie (https://en.wikipedia.org/wiki/Trie) and check using that?
sort the word list based on word length
foreach of the word, if the word is not in the trie, add it to the trie. otherwise, this is either a dup or a compound word
output the trie into a list of words using DFS.
step 1 make sure that when u check for a compound word, its simple word is already in the trie.
I didn't try to find the bug in your code, but rather wrote my own impl using a simple loop and a recursive helper method:
private static String[] find(String[] array) {
Set<String> words = new LinkedHashSet<>(Arrays.asList(array));
Set<String> otherWords = new HashSet<>(words);
for (Iterator<String> i = words.iterator(); i.hasNext(); ) {
String next = i.next();
otherWords.remove(next);
if (isCompound(next, otherWords)) {
i.remove();
} else {
otherWords.add(next);
}
}
return words.stream().toArray(String[]::new);
}
private static boolean isCompound(String string, Set<String> otherWords) {
if (otherWords.contains(string)) {
return true;
}
for (String word : otherWords) {
if (string.startsWith(word)) {
return isCompound(string.replaceAll("^" + word, ""), otherWords);
}
if (string.endsWith(word)) {
return isCompound(string.replaceAll(word + "$", ""), otherWords);
}
}
return false;
}
See live demo.
This produces your desired output, which requires preserving word order.
Explanation
A compound word is comprised solely of other words in the list. Importantly, this implies that compound words both start and end with other words. Rather than search for other words at every position in a word, we can use this fact to only check the start/end , which greatly simplifies the code.
Thus: for each word in the list, if it start/ends with another word, remove that word and repeat the process until there's nothing left, at which point you know the word is compound.
A set of "other words", which is the full set with the current word removed, is passed to the helper method to further simplify the code.
Here is my straightforward n^2 solution:
static String[] simpleWords(String[] words) {
String[] result;
HashSet<Integer> map = new HashSet<>();
for(int i = 0; i < words.length; i++) {
String word = words[i];
for(int j = 0; j < words.length; j++) {
if(j != i) {
word = word.replaceAll(words[j], "");
}
}
if(!word.equals("")) {
map.add(i);
}
}
result = new String[map.size()];
int i = 0;
for(int index: map) {
result[i] = words[index];
i++;
}
return result;
}
I have two different arrays and they both contain the same type of element. The elements are obtained from user input. And the arrays can store fixed size of element.
*Let's say that fisrt one is basically an arrayList and it adds the user input into the list.
The second one is simply an array which also gets value from user input. All the elements of second array are also contained in the first array and the length of this array is less than the first array*
Now I want to print an array which is the result of first array-second array.
This is the program I am working on right now. You may avoid this coding just to give me a theoritical concept for doing that.
package issuetracking;
import java.util.*;
public class IssueTrackingObject {
ArrayList<String> crIss = new ArrayList<String>();
Scanner input = new Scanner(System.in);
boolean crIss_bool;
int numOfSolvedIss;
private String[] solvedIss;
//lets user create some issues and add them into an arrayList
public void createIssue() {
System.out.println("Enter 5 issues: ");
for (int i = 0; i < 5; i++) {
System.out.println("Issue " + (i + 1 + ": "));
crIss_bool = crIss.add(input.nextLine());
}
}
//Let user mark some issues as solved (which are already in the list that the user has just created)
public void solvedIssue() {
System.out.println("How many solved issue you have(Must be less than 5): ");
numOfSolvedIss = input.nextInt();
solvedIss = new String[numOfSolvedIss];
for (int k = 0; k < numOfSolvedIss; k++) {
System.out.print("Enter solved issue(REMEMBER THAT THE SOLVED ISSUE MUST BE FROM ONE OF THEM YOU ALREADY HAVE CREATED)no. " + (k + 1) + ": ");
solvedIss[k] = input.next();
}
}
public void printUnsolvedIssue() {
//print out the elements of createIssue() that doesn't belong to the solvedIssue()
}
You can use a simple solution like this:
for (String solved : solvediss) {
if (solved != null) crIss.remove(solved);
}
This will remove all the Strings from the list that are in the array.
Of course, you could also do this.
crIss.removeAll(Arrays.asList(solvediss));
Go over your second array and use the remove method in arraylist to remove every element of second array from the first.
for (int i = 0; i < solvedIss.length; i++) {
crIss_bool = crIss.remove(solvedIss[i]);
}
Removing the elements might be the simplest method in this case, but it causes original list to change.
If you do not wish to have any destructive modification to the original list, you could perform a simple search like following.
for (String issue : crIss) {
bool isUnSolved = true;
for (String solvedIssue : solvedIss) {
if (issue.equals(solvedIssue)) {
isUnSolved = false;
break;
}
}
if (isUnSolved) {
// Print the 'issue' or do whatever you want to do with it.
}
}
Hope this helps.
Good luck.
I build removing duplicates, however I'm thinking how to use a method that removes the duplicate elements from an array list of integers using the following header:
public static void removeDuplicate(ArrayList<Integer> list)
write a test program that prompts the user to enter 10 integers to a list and
displays the distinct integers separated by exactly one space.
import java.util.ArrayList;
import java.util.Scanner;
public class RemoveDuplicates {
public static void main(String[] args){
ArrayList<Integer>list = new ArrayList<Integer>();
Scanner input = new Scanner (System.in);
System.out.print("Enter integers (input ends with 0): ");
int value;
do{
value = input.nextInt();
if(!list.contains(value)&& value !=0)
list.add(value);
}while (value !=0);
input.close();
for (int i = 0; i < list. size(); i++)
System.out.print(list.get(i) + " ");
}
}
It's my code,please modifying please, how to use method and test .
If I understand correctly, you should implement a method with this header
public static void removeDuplicate(ArrayList<Integer> list)
judging by its name, I'd say that method should remove the duplicates from the list and not (as you are doing it right now) the do-while-loop during input.
So first remove the check in your loop (if(!list.contains(value)&& value !=0)) and just add every number the user types to the list.
Then you can make a call to the method removeDuplicate(list);. If you want you can add this call in your loop and it will be executed after every input or you execute it just once when the input is closed.
Now implementing the method:
public static void removeDuplicate(ArrayList<Integer> list) { // this is the header you need to use
The problem here is, the method knows the list but not the element that is a possible duplicate. So you have to look for it
for (int i = 0; i < list.size(); i++) { // iterate through every element in the list
Integer current = list.get(i); // for convenience, save the current list item in a variable
So, you check every integer in the list – one by one.. but if you want to know if the integer exists a second time, you have to search the tail of the list. Meaning you have to check the sublist after i.
List sublist = list.subList(i + 1, list.size()); // the sublist with all elements of the list from i+1 to the end
your list.contains(value) line is correct, you can use it here as well. Only now you call it on the sublist
if(sublist.contains(current)){ // checks if the number is in the sublist
sublist.remove(current); // removes the number from the sublist
}
This however would only remove the first duplicate. Alternatively, you can remove every item in the list that equals the current integer:
while (sublist.contains(current)) {
sublist.remove(current);
}
And that's it. Your method is finished.
}
}
It is finished because you are actually working on the one and only list in your program. Even when you remove an integer from your sublist, it is actually removed from the sublist and the real list (the sublist is just a reference, and not an actual list on its own)
EDIT
for your convenience here the complete code with both methods. If you compare the code to yours, you'll see that there is not much different:
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<Integer>();
Scanner input = new Scanner(System.in);
System.out.print("Enter integers (input ends with 0): ");
int value;
do {
value = input.nextInt();
if (value != 0) { // this changed: add every number except 0
list.add(value);
}
} while (value != 0);
input.close();
removeDuplicate(list); // here you make the call for the new method
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + " ");
}
}
// and this is the new method
public static void removeDuplicate(ArrayList<Integer> list) {
for (int i = 0; i < list.size(); i++) {
Integer current = list.get(i);
List sublist = list.subList(i + 1, list.size());
while (sublist.contains(current)) {
sublist.remove(current);
}
}
}
If you don't want duplicate, use a collection that implements the Set interface (http://docs.oracle.com/javase/7/docs/api/java/util/Set.html) instead of an array list.
Let's say I got this array:
String[][]array = new String[5][5];
array[2][2] = desperate;
Would it be possible to find whether
String s = "desperate"; - equals any array element without using a for loop, and without having to manually enter the row column combination of the array assigned the value "desperate"?
while loop instead of for loop
int i = 0;
int j = 0;
while (i < n)
{
while (j < m)
{
if (array[i][j].equals("..."))
{
///
}
j++;
}
i++;
}
Use enhanced-for loop: -
String [][] array = new String[2][2];
array[1][1] = "desperate";
array[0][1] = "despee";
array[1][0] = "despete";
array[0][0] = "dete";
for (String[] innerArr: array) {
for (String value: innerArr) {
if (value.equals("desperate")) {
System.out.println(value + " == desperate");
}
}
}
Output: - desperate == desperate
A better way that I would suggest is to use ArrayList<String> to store your items.. Then you can just call contains() method to check whether the list contains that element..
List<String> listString = new ArrayList<String>();
listString.add("desperate");
listString.add("despe");
if (listString.contains("desperate")) {
System.out.println("True");
}
Output: - True
Assuming that you can't (for any reasons) change your array to another collection type:
String[][]array = new String[5][5];
array[2][2] = "desperate";
public boolean contains(String str){
return new HashSet<String>((List<String>)Arrays.asList(array)).contains(str);
}
Better than transforming it to a List since HashSet's contains() method is O(1) and the one from List is O(n).
The only way to avoid using a loop (and it not clear why you would want to) is to use a Map which you pre-build with all the strings and indexes.
My requirement is to enter strings into an array which are not in the array. I also need to maintain fixed indexes, as this array will be used with other data structure with a one-to-one relation with each index. At present i am using the ArrayList class and checking with the indexOf () method to check if it exists first, if not then add it into the list with the add () method with one argument. I am not familiar to the classes in java, and therefore could not understand how can i implement it with HashMap or something else (trie or else), which will make the loading process fast .
Do the indexOf () in ArrayList makes a sequential search ?
My point is to reduce the processing time when loading the words into the array, with not inserting duplicates, and maintain fixed index of the elements. If a word tested is already in the array, then the index in which it is already inserted is required, as this index is needed to index into some other structure and do some processing. Any suggestions to make this process better?
UPDATE
There is an array, i have some documents from where i need to scan each word and find unique words in the documents. But also i need to count the number of duplicates. Stated in other way, i need to count the term frequencies of the unique terms occurring in the documents. I am maintaining a ArrayList<Integer[]> of term frequency (number of terms x number of docs). I am fetching one word and then checking if it is in the word list with the indexOf () method. If it is not present in the word list, then i am inserting the word into the list, and allocating a new row in the 2d array (the Array<Integer[]>) and then setting the count of the term element in 2d array to 1. If the word is already in the word array, then i use the index of the word in the array to index in the row of the Array<Integer[]> matrix, and use the current under processing document number to get to the cell and increment the count.
My question is to reduce the indexOf () processing time for each word i am currently using. I need to get the index of the word in the word array if it is already in there, and if it is not in there then i need to insert it into the array dynamically.
Sample Code
import java.io.*;
import java.util.ArrayList;
import static java.lang.Math.log;
class DocumentRepresentation
{
private String dirPath;
private ArrayList<String> fileNameVector;
private ArrayList<String> termVector;
private ArrayList<Integer[]> tf; /* store it in natural 2d array */
private Integer df[]; /* do normal 1d array */
private Double idf[]; /* do normal 1d array */
private Double tfIdf[][]; /* do normal 2d array */
DocumentRepresentation (String dirPath)
{
this.dirPath = dirPath;
fileNameVector = new ArrayList<String> ();
termVector = new ArrayList<String> ();
tf = new ArrayList<Integer[]> ();
}
/* Later sepatere the internal works */
public int start ()
{
/* Load the files, and populate the fileNameVector string */
File fileDir = new File (dirPath);
int fileCount = 0;
int index;
if (fileDir.isDirectory () == false)
{
return -1;
}
File fileList[] = fileDir.listFiles ();
for (int i=0; i<fileList.length; i++)
{
if (fileList[i].isFile () == true)
{
fileNameVector.add (fileList[i].getName ());
// System.out.print ("File Name " + (i + 1) + ": " + fileList[i].getName () + "\n");
}
}
fileCount = fileNameVector.size ();
for (int i=0;i<fileNameVector.size (); i++)
{
System.out.print ("Name " + (i+1) + ": " + fileNameVector.get (i) + "\n");
}
/* Bind the files with a buffered reader */
BufferedReader fileReaderVector[] = new BufferedReader [fileCount];
for (int i=0; i<fileCount; i++)
{
try
{
fileReaderVector[i] = new BufferedReader (new FileReader (fileList[i]));
}
/* Not handled */
catch (FileNotFoundException e)
{
System.out.println (e);
}
}
/* Scan the term frequencies in the tf 2d array */
for (int i=0; i<fileCount; i++)
{
String line;
try
{
/*** THIS IS THE PLACE OF MY QUESTION **/
while ((line = fileReaderVector[i].readLine ()) != null)
{
String words[] = line.split ("[\\W]");
for (int j=0;j<words.length;j++)
{
if ((index = termVector.indexOf (words[j])) != -1)
{
tf.get (index)[i]++;
/* increase the tf count */
}
else
{
termVector.add (words[j]);
Integer temp[] = new Integer [fileCount];
for (int k=0; k<fileCount; k++)
{
temp[k] = new Integer (0);
}
temp[i] = 1;
tf.add (temp);
index = termVector.indexOf (words[j]);
}
System.out.println (words[j]);
}
}
}
/* Not handled */
catch (IOException e)
{
System.out.println (e);
}
}
return 0;
}
}
class DocumentRepresentationTest
{
public static void main (String args[])
{
DocumentRepresentation docSet = new DocumentRepresentation (args[0]);
docSet.start ();
System.out.print ("\n");
}
}
Note: code is snipped to keep the focus on the question
LinkedHashMap can satisfy all your requirements at once, with good performance characteristics.
The keys would be your items and the values would be the indices. If you insert the elements in the order of increasing indices, then iterating over the map would also return the elements in the order of increasing indices.
Here is some sample code:
LinkedHashMap<Item,Integer> map = new LinkedHashMap<Item,Integer>();
Get the item's index:
Integer index = map.get(item);
if (index != null) {
// already in the map; use `index'
} else {
// not in the map
}
Add item with the next index:
if (!map.containsKey(item)) {
map.put(item, map.size());
}
Iterate over the elements in the order of increasing indices:
for (Entry<Item,Integer> e : map.entrySet()) {
Item item = e.getKey();
int index = e.getValue();
...
}
What this can't do efficiently is get the value at the specific index, but my reading of your question indicates that you don't actually need this.
ArrayList.indexOf() does a linear search, so it's O(n).
If it really has to go into an ArrayList, you could create two collections, ArrayList and HashSet. Add and remove elements to both collections. Before adding, call HashSet.contains() to see if the element already exists.
Encapsulate your ArrayList and HashSet in its own class.
If you want to leave the ArrayList you can have an HashSet as support, with the cost of the double of the memory.
You can use HashSet.add() if return true you can add also the element to the ArrayList