Searching through parallel arrays - java

I am trying to write a program to search through parallel arrays on stores double values one string, I need to be able to search either by a certain value returning all values equal to or more than in the array and the same index in the string array.
I was able to write methods to sort the array like this
public static void Sortstrength(Double[] strength, String[] names, int sel) //method to sort beers alphabetically up and down
{
String tmpStr; //temp string to help sort array
Double tmpDbl; //temp double to help sort array
if (sel == 1) //sort by names ascending if sel int = 1
{
for (int t = 0; t < strength.length - 1; t++) {
for (int i = 0; i < strength.length - 1; i++) {
if (strength[i].compareTo(strength[i + 1]) > 0) {
tmpStr = names[i];
tmpDbl = strength[i];
names[i] = names[i + 1];
strength[i] = strength[i + 1];
names[i + 1] = tmpStr;
strength[i + 1] = tmpDbl;
}
}
}
for (int i = 0; i < names.length; i++) {
System.out.printf("%-15s %s \n", names[i], strength[i]);
}
} else //sort by names descending
{
for (int t = 0; t < strength.length - 1; t++) {
for (int i = 0; i < strength.length - 1; i++) {
if (strength[i].compareTo(strength[i + 1]) < 0) {
tmpStr = names[i];
tmpDbl = strength[i];
names[i] = names[i + 1];
strength[i] = strength[i + 1];
names[i + 1] = tmpStr;
strength[i + 1] = tmpDbl;
}
}
}
for (int i = 0; i < names.length; i++) {
System.out.printf("%-15s %s \n", names[i], strength[i]);
}
}
}
I have no clue how to alter this if it is even possible to do so but any help would be appreciated as I am pretty stuck
Thanks for any help.

Create a class Beer holding a double and a string value. Add the Beers to a list and use a Comperator to sort. Output the list.
public class Beer
{
private final double strength;
private final String name;
public Bear(final String name, final double strength)
{
this.strength = strength;
this.name = name;
}
public String getName() { return name; }
public double getStrength() { return strength; }
}

Sounds like what you actually want is a sorted map from Doubles to Strings. Use TreeMap:
private final TreeMap<Double, String> map = new TreeMap<>(); //for descending order use .reverseMap()
public SortedMap<Double, String> getKeysAbove(double key) {
return map.subMap(key, Double.POSITIVE_INFINITY);
}
public static void exampleUsage() {
Mapper mapper = new Mapper(); //or whatever class name
mapper.map.put(1D, "Goodbye");
mapper.map.put(2D, "Hello, ");
mapper.map.put(3D, "World!");
for (Entry<Double, String> e : mapper.getKeysAbove(1.5).entrySet()) {
System.out.println(e.getKey() + ": " + e.getValue());
}
}

To search your data with strength array sorted in ascended order use binarySearch:
double threshold = 2.0d;
int index = Arrays.binarySearch(strength, threshold);
if (index < 0) {
index = -index - 1;
}
since your two arrays are "synced" this index will match your String array.
Note, you could implement "binding" of strength/names data and their sorting much cleaner and more effectively in at least two different ways :
use Map to store your bindings assuming your strength keys are unique, and then sorting by keys.
Use helper POJO class MyData holding both strength and name, as in Hannes's answer and sort array of MyData objects supplying custom Comparator, then do binarySearch with same comparator to find index according to your requirements.

Related

Maximum repeated String in an array

The problem is
how to get the maximum repeated String in an array using only operations on the arrays in java?
so i got into this question in a test and couldn't figure it out.
lets suppose we have an array of string.
str1[] = { "abbey", "bob", "caley", "caley", "zeeman", "abbey", "bob", "abbey" }
str2[] = { "abbey", "bob", "caley", "caley", "zeeman", "abbey", "bob", "abbey", "caley" }
in str1 abbey was maximum repeated, so abbey should be returned and
in str2 abbey and caley both have same number of repetitions and hence we take maximum alphabet as the winner and is returned(caley here).
c > a
so i tried till
import java.util.*;
public class test {
static String highestRepeated(String[] str) {
int n = str.length, num = 0;
String temp;
String str2[] = new String[n / 2];
for (int k = 0;k < n; k++) { // outer comparision
for (int l = k + 1; l < n; l++) { // inner comparision
if (str[k].equals(str[l])) {
// if matched, increase count
num++;
}
}
// I'm stuck here
}
return result;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("enter how many votes");
int n = sc.nextInt();
String[] str = new String[n];
for (int i = 0; i < n; i++) {
Str[i] = sc.nextLine();
}
String res = highestRepeated(str);
System.out.println(res + " is the winner");
}
}
so, how should i take the count of occurrence of each string with and attach it with the string itself.
All this, without using a map and any hashing but just by using arrays?
Here is a (unpolished) solution:
static String highestRepeated(String[] str) {
String[] sorted = Arrays.copyOf(str, str.length);
Arrays.sort(sorted, 0, sorted.length, Comparator.reverseOrder());
String currentString = sorted[0];
String bestString = sorted[0];
int maxCount = 1;
int currentCount = 1;
for (int i = 1 ; i < sorted.length ; i++) {
if (currentString.equals(sorted[i])) {
currentCount++;
} else {
if (maxCount < currentCount) {
maxCount = currentCount;
bestString = currentString;
}
currentString = sorted[i];
currentCount = 1;
}
}
if (currentCount > maxCount) {
return currentString;
}
return bestString;
}
Explanation:
Sort the array from highest to lowest lexicographically. That's what Arrays.sort(sorted, 0, sorted.length, Comparator.reverseOrder()); does. we sort in this order because you want the largest string if there are multiple strings with the same number of repeats.
Now we can just count the strings by looping through the array. We don't need a hash map or anything because we know that there will be no more of a string in the rest of the array when we encounter a different string.
currentString is the string that we are currently counting the number of repeats of, using currentCount. maxCount is the number of occurrence of the most repeated string - bestString - that we have currently counted.
The if statement is pretty self-explanatory: if it is the same string, count it, otherwise see if the previous string we counted (currentCount) appears more times than the current max.
At the end, I check if the last string being counted is more than max. If the last string in the array happens to be the most repeated one, bestString won't be assigned to it because bestString is only assigned when a different string is encountered.
Note that this algorithm does not handle edge cases like empty arrays or only one element arrays. I'm sure you will figure that out yourself.
another version
static String lastMostFrequent(String ... strs) {
if (strs.length == 0) return null;
Arrays.sort(strs);
String str = strs[0];
for (int longest=0, l=1, i=1; i<strs.length; i++) {
if (!strs[i-1].equals(strs[i])) { l=1; continue; }
if (++l < longest) continue;
longest = l;
str = strs[i];
}
return str;
}
change in
if (++l <= longest) continue;
for firstMostFrequent
you can't use == to check if two strings are the same.
try using this instead:
if (str[k].equals(str[l])) {
// if matched, increase count
num++;
}

deleting an object from an array of objects in java

import java.util.StringTokenizer;
class Count {
int count;
String name;
void SetCount(int c, String n) {
this.count = c;
this.name = n;
}
void Show() {
System.out.print("Word= " + name);
System.out.print(" Count= " + count);
System.out.println();
}
}
class Contains2 extends Count {
public static void main(String args[]) {
String s = "Hello this program will repeat itself for this useless purpose and will not end until it repeats itself again and again and again so watch out";
int i, c2, j;
StringTokenizer st = new StringTokenizer(s, " ");
c2 = st.countTokens();
String[] test = new String[c2];
Count[] c = new Count[c2];
for (i = 0; i < c2; i++) {
c[i] = new Count();
}
i = 0;
while (st.hasMoreTokens()) {
String token = st.nextToken();
test[i] = token;
c[i].SetCount(0, test[i]);
i++;
}
for (i = 0; i < c2; i++) {
for (j = 0; j < c2; j++) {
if (c[i].name.equals(test[j]))
c[i].count += 1;
}
}
for (i = 0; i < c2; i++) {
c[i].Show();
}
}
}
so i made this small program to count the number every word was repeated in a paragraph. its working as planned but now i am getting duplicates of every word since i made separate objects for each and printing them all. so is there any way i could delete the duplicate words i mean deleting those objects based on their names. i can set them to null but it would still print them so i just wanna get rid of them or skip them somehow
You cannot adjust the size of a array once it's created. You could only create a new array with a different size and copy the non-null elements.
You could also set elements to null and just ignore those elements for printing...
for (i = 0; i < c2; i++) {
if (c[i] != null)
c[i].Show();
}
An alternative would be using a List, which allows you to remove elements.
Alternatively a Map<String, Integer> mapping from a word to the count could be used. In this case you don't even need the Count class:
String s = "Hello this program will repeat itself for this useless purpose and will not end until it repeats itself again and again and again so watch out";
StringTokenizer st = new StringTokenizer(s, " ");
Map<String, Integer> map = new HashMap<>();
while (st.hasMoreTokens()) {
map.merge(st.nextToken(), 1, Integer::sum);
}
for (Map.Entry<String, Integer> e : map.entrySet()) {
System.out.print("Word= " + e.getKey());
System.out.print(" Count= " + e.getValue());
System.out.println();
}

Bubble Sort Array with unknown length (Java)?

This is Java through my university so there are some components in here that you may not know. Do not pay attention for them because they are not the problem.
My compiler made me initialize my array to null. I then try to run the rest of my program and it said that it is supposed to work with an array that has actual values in it. My question is how should I go about doing this?
private static String[] alphabetSort(Map<String, Integer> mapWord) {
String[] ordered = null;
Map<String, Integer> copy = mapWord;
for (int i = 0; i < copy.size(); i++) {
Map.Pair<String, Integer> pair = copy.removeAny();
String key = pair.key();
ordered[i] = key;
}
boolean flag = true;
String temp;
while (flag) {
flag = false;
for (int j = 0; j < ordered.length; j++) {
String test1 = ordered[j];
String test2 = ordered[j + 1];
if (test1.compareTo(test2) < 0) {
temp = ordered[j];
ordered[j] = ordered[j + 1];
ordered[j + 1] = temp;
flag = true;
}
}
}
return ordered;
}
Arrays have fixed-size. You have to pick a size when you're creating it.
Try:
String[] ordered = new String[mapWord.size()];

Want to count occurances of Strings in Java

So I have a .txt file which I am calling using
String[] data = loadStrings("data/data.txt");
The file is already sorted and essentially looks like:
Animal
Animal
Cat
Cat
Cat
Dog
I am looking to create an algorithm to count the sorted list in java, without using any libraries like Multisets or without the use of Maps/HashMaps. I have managed so far to get it print out the top occurring word like so:
ArrayList<String> words = new ArrayList();
int[] occurrence = new int[2000];
Arrays.sort(data);
for (int i = 0; i < data.length; i ++ ) {
words.add(data[i]); //Put each word into the words ArrayList
}
for(int i =0; i<data.length; i++) {
occurrence[i] =0;
for(int j=i+1; j<data.length; j++) {
if(data[i].equals(data[j])) {
occurrence[i] = occurrence[i]+1;
}
}
}
int max = 0;
String most_talked ="";
for(int i =0;i<data.length;i++) {
if(occurrence[i]>max) {
max = occurrence[i];
most_talked = data[i];
}
}
println("The most talked keyword is " + most_talked + " occuring " + max + " times.");
I want rather than just to get the highest occurring word perhaps the top 5 or top 10.
Hope that was clear enough. Thanks for reading
Since you said you dont want to use some kind of data structure i think that you can do something like this, but it is not performant.
I usually prefer to store index rather than values.
ArrayList<String> words = new ArrayList();
int[] occurrence = new int[2000];
Arrays.sort(data);
int nwords = 0;
occurrence[nwords]=1;
words.add(data[0]);
for (int i = 1; i < data.length; i ++ ) {
if(!data[i].equals(data[i-1])){ //if a new word is found
words.add(data[i]); //put it into the words ArrayList
nwords++; //increment the index
occurrence[nwords]=0; //initialize its occurrence counter
}
occurrence[nwords]++; //increment the occurrence counter
}
int max;
for(int k=0; k<5; k++){ //loop to find 5 times the most talked word
max = 0; //index of the most talked word
for(int i = 1; i<words.size(); i++) { //for every word
if(occurrence[i]>occurrence[max]) { //if it is more talked than max
max = i; //than it is the new most talked
}
}
println("The most talked keyword is " + words.get(max) + " occuring " + occurence[max] + " times.");
occurence[max]=0;
}
Every time I find the value with the higher occurence value, i set his occurrence counter to 0 and I reiterate again the array, this for 5 times.
If you cannot use Guava's Multiset, then you can implement an equivalent yourself. Basically, you just need to create a Map<String, Integer>, which keeps track of counts (value) per each word (key). This means changing this
ArrayList<String> words = new ArrayList<String>();
// ...
for (int i = 0; i < data.length; i ++ ) {
words.add(data[i]); //Put each word into the words ArrayList
}
into this:
Map<String, Integer> words = new HashMap<String>();
// ...
for (String word : data) {
Integer count = words.get(word);
words.put(word, (count != null : count.intValue() + 1 ? 1));
}
After you've filled the map, just sort it by the values.
If you cannot use a Map either, you can do the following:
First, create a wrapper class for your word counts:
public class WordCount implements Comparable<WordCount> {
private String word;
private int count;
public WordCount(String w, int c) {
this.word = w;
this.count = c;
}
public String getWord() {
return word;
}
public int getCount() {
return count;
}
public void incrementCount() {
count++;
}
#Override
public int compareTo(WordCount other) {
return this.count - other.count;
}
}
Then, change your code to store WordCount instances in your list (instead of Strings):
ArrayList<WordCount> words = new ArrayList<WordCount>();
// ...
for (String word : data) {
WordCount wc = new WordCount(word, 1);
boolean wordFound = false;
for (WordCount existing : words) {
if (existing.getWord().equals(wc.getWord())) {
existing.incrementCount();
wordFound = true;
break;
}
}
if (!wordFound) {
words.add(wc);
}
}
Finally, after populating the List, simply sort it using Collections.sort(). This is easy because the value objects implement Comparable:
Collections.sort(words, Collections.reverseOrder());
You could try something simple like this..
int count = 0;
for( int i = 0; i < words.size(); i++ ){
System.out.printf("%s: ", words.get( i ));
for( int j = 0; j < words.size(); j++ ) {
if( words.get( i ).equals( words.get( j ) ) )
count++;
}
System.out.printf( "%d\n", count );
}

Java code with tests - infinite loop?

I try to get the relationship between people. However, when I run unit test, the test runs forever, it doesn't get the result and my CPU usage was high.
Could someone see what's wrong with my code?
The string relations are multiple line inputs of string with in the format of "A , B C , D" where A is the parent of B and C is the parent of D.
This is the default constructor for the code and the input in string format. We don't need to check if the format is correct:
public SeeRelations(String relations){
this.relations = relations;
}
This the helper function to get each line of the string from the formatted input:
//helper function to get each line of the string
private ArrayList<String> lineRelations(){
int i;
ArrayList<String> lineRelations = new ArrayList<String>();
String[] lines = relations.split("\n");
for(i = 0; i < lines.length; i++){
lineRelations.add(lines[i]);
}
return lineRelations;
}
This is the function to put all the relations from the input formatted string to arraylists:
//helper function to put each of the relationship in arraylists
private ArrayList<ArrayList<String>> allRelations(){
int i;
ArrayList<ArrayList<String>> allRelations = new ArrayList<ArrayList<String>>();
ArrayList<String> lineRelations = lineRelations();
for(i = 0; i < lineRelations.size(); i++){
ArrayList<String> eachLine = new ArrayList<String>(Arrays.asList(lineRelations.get(i).split("\\s*,\\s*")));
allRelations.add(eachLine);
}
return allRelations;
}
This is the method to check if the input name exists:
//helper function to see if the name exist for seeRelations()
private boolean hasThisName(String name){
ArrayList<ArrayList<String>> allRelations = allRelations();
int i;
int j;
for(i = 0; i < allRelations.size(); i++){
for(j = 0; j < allRelations.get(i).size(); j++){
if(name.equals(allRelations.get(i).get(j))){
return true;
}
}
}
return false;
}
This is the function to get the generation number between two people:
//helper function to get Generation number of seeRelations()
private int getGenerationNum(String person, String ancestor){
ArrayList<ArrayList<String>> allRelations = allRelations();
String name;
int i;
int j;
int generationNum = 0;
for(i = 0, j = 0, name = ancestor; i < allRelations.size(); i++){
if(name.equals(allRelations.get(i).get(0)) && !person.equals(allRelations.get(i).get(1))){
generationNum++;
ancestor = allRelations.get(i).get(1);
i = 0;
j = 1;
}
else if(ancestor.equals(allRelations.get(i).get(0)) && person.equals(allRelations.get(i).get(1))){
generationNum++;
j = 1;
break;
}
}
if(j == 0){
return 0;
}
else{
return generationNum;
}
}
This is the method to get multiple of "great" for the final output:
private String great(int num){
int i;
String great = "";
for(i = 0; i < num; i++){
great += "great";
}
return great;
}
This is my final method to check the relationship between two people:
public String SeeRelations(String person, String ancestor){
int generationNum = getGenerationNum(person, ancestor);
String great = great(generationNum - 2);
if(!(hasThisName(person) && hasThisName(ancestor))){
return null;
}
else{
if(generationNum == 0){
return null;
}
else if(generationNum == 1){
return ancestor + " is the parent of " + person;
}
else if(generationNum == 2){
return ancestor + " is the grandparent of " + person;
}
else{
return ancestor + " is the" + " " + great +"grandparent of " + person;
}
}
}
This is my test cases, And it runs forever and couldn't get a result
public class FamilyTreeTest {
#Test
public void testSeeRelations() {
FamilyTree relation2 = new FamilyTree("John Doe , Mary Smith" + "\n" + "Martin Weasel , John Doe");
assertEquals("Martin Weasel is the grandparent of Mary Smith", familyTree2.SeeRelations("Mary Smith", "Martin Weasel"));
}
for(i = 0, j = 0, name = ancestor; i < allRelations.size(); i++){
if(name.equals(allRelations.get(i).get(0)) && !person.equals(allRelations.get(i).get(1))){
generationNum++;
ancestor = allRelations.get(i).get(1);
i = 0;
j = 1;
}
else if(ancestor.equals(allRelations.get(i).get(0)) && person.equals(allRelations.get(i).get(1))){
generationNum++;
j = 1;
break;
}
}
here you have your faulty lines
in your case your ancestor/name is "Martin Weasel" given relation for martin is "John Doe", but you are looking for mary smith so name.equals(allRelations.get(i).get(0)) && !person.equals(allRelations.get(i).get(1))) this is true and this i = 0; makes your loop starts from beginning
what you could do, try to create object person
ie
class Person{
String name;
List childrens;
List parents;
...
}
then just do simple tree walker
int SearchDown(Person person, String searchedRelation,int generation)
{
if person.getName().equals(searchedRelation())
return generation;
for (Person child: person.getChildren())
{
int generation = SearchDown(child, searchedRelation, generation+1);
if (generation!=-1) return generation;
}
return -1;
}
etc...
i'm really finding this way much easier to deal with all types of trees

Categories

Resources