I am having trouble finding the most and least used String in an ArrayList. The program should go through a file of Strings and count how many multiple strings there are in the list. Then print the least and most used name in the list. The ArrayList Part is finished. It is just finding the most and least common name I am having trouble with. I have no idea how to even start with it. This is what I have found online but it is not working.
Map<String, Integer> dogNames = new HashMap<>();
for (Dog dog : dogs) {
Integer value = dogNames.get(dog);
if (value == null) {
value = 0;
}
value++;
dogNames.put(dog.getName(), value);
}
int leastCommon = Integer.MAX_VALUE;
String leastCommonName = null;
for (String name : dogNames.keySet()) {
int value = dogNames.get(name);
if (value < leastCommon) {
leastCommon = value;
leastCommonName = name;
}
}
System.out.println("Least common (" + leastCommon + ") is " + leastCommonName);
The problem with your code seems to be in this line:
Integer value = dogNames.get(dog);
Your map holds dog names (String), but you are getting the entry for the Dog, which does not exist! Thus, value stays 0 even if you've seen that name before. If you fix this, you code should work.
Instead of your loop for searching the least common name, you could also define a custom Comparator based on the counts in the map and then use Collections.min and Collections.max:
Comparator<Dog> comp = new Comparator<Dog>() {
#Override
public int compare(Dog o1, Dog o2) {
return Integer.compare(dogNames.get(o1.getName()), dogNames.get(o2.getName()));
}
};
System.out.println("least " + Collections.min(dogs, comp));
System.out.println("most " + Collections.max(dogs, comp));
With Java 8, you can make it even shorter, using Comparator.comparing:
List<Dog> dogs = ...
Map<String, Integer> dogNames = new HashMap<>();
dogs.forEach(dog -> dogNames.put(dog.getName(), dogNames.getOrDefault(dog.getName(), 0) + 1));
Comparator<Dog> comp = Comparator.comparing(d -> dogNames.get(d.getName()));
System.out.println("least " + Collections.min(dogs, comp));
System.out.println("most " + Collections.max(dogs, comp));
Or even shorter, using Collections.frequency instead of building your own map, and using that to compare. Note, however, that this will be wasteful if the list is very long, since this will search the list each time anew instead of caching the counts in the map.
List<Dog> dogs = ...
Comparator<Dog> comp = Comparator.comparing(d -> Collections.frequency(dogs, d.getName()));
System.out.println("least " + Collections.min(dogs, comp));
System.out.println("most " + Collections.max(dogs, comp));
Your code should look something like this...
Map<String,int> frequencyOfDogNames = new HashMap<String,int>();
for(String dogName:dogNames) {
if(frequencyOfDogNames.contains(dogName)) {
continue;
}
frequencyOfDogNames.put(dogName, Collections.frequency(dogs, "dogName"));
}
This will give you the map of all the names with the occurrences.
Now we should loop thought the map to see which one are the max and min...
int leastCommon = Integer.MAX_VALUE;
int mostCommon = 0;
String leastCommonName, mostCommonName;
int occurrence;
for(String dogName: frequencyOfDogNames.keySet()) {
occurrence = frequencyOfDogNames.get(dogName);
if(leastCommon > occurrence){
leastCommon = occurrence;
leastCommonName = dogName;
}
if(mostCommon < occurrence){
mostCommon = occurrence;
mostCommonName = dogName;
}
}
Related
I have a csv file of the stock exchange I am taking data from, reading it and printing it under certain conditions. One of the conditions under which I must display them is to display the lowest value of a share of each share. My code only shows the first record of each of the three stocks, and does not show the lowest value of these stocks. Where am I going wrong in my filtering using the filter?
My code
private static void smallestPriceByStock(List<String> stocks) throws ParseException {
List<StockPrice> stockPrices = new ArrayList<>();
HashSet<String> stock = new HashSet<>();
SimpleDateFormat sdfOriginal = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdfNovaData = new SimpleDateFormat("dd/MM/yyyy");
try {
for (String s : stocks) {
String[] array = a.split(",");
String name = array[0];
String date = array[1];
Date dataOriginal = sdfOriginal.parse(date);
float fechamento = Float.parseFloat(array[2]);
long volume = Long.parseLong(array[3]);
StockPrice stockPrice = new StockPrice(nome, dataOriginal, price, volume);
stockPrices.add(stockPrice);
Stock st = new Stock(name);
st.addStockPrice(stockPrice);
stock.add(st.getNome());
}
System.out.println("Menores fechamentos");
List<Cotacao> cotas = new ArrayList<>();
double smallestValue = 0;
for(String a : acao) {
for (Cotacao c : cotacoes) {
if (c.getNome().contains(a)) {
smallestValue = cotacoes.stream()
.filter(x -> x.getNome() == a)
.mapToDouble(x -> x.getValue()).summaryStatistics().getMax();
System.out.println("Stock: " + c.getNome() + ", " + "Price: " + smallestValue + ", " + "Date: " + sdfNovaData.format(c.getData()));
break;
}
}
}
for(Cotacao cotacao : cotas){
System.out.println(cotacao.getNome());
}
System.out.println();
} catch (UnsupportedOperationException | ParseException e) {
throw new UnsupportedOperationException("calculaMenorFechamentoPorAcao não implementado", e);
}
}
A little sampe of the csv file
Acao,Data,Fechamento,Volume
OGXP3,2013-01-01,4.38,0
OGXP3,2013-01-02,4.76,45904000
OGXP3,2013-01-03,4.90,38143400
PETR4,2013-01-02,19.69,30182600
PETR4,2013-01-03,20.40,30552600
PETR4,2013-01-04,20.43,36141000
PETR4,2013-01-07,20.08,28069600
VALE5,2013-01-01,40.87,0
VALE5,2013-01-02,42.60,18515700
VALE5,2013-01-03,42.09,15001800
VALE5,2013-01-04,41.36,26351900
You are making this more complicated that it needs to be. The common pseudo code for finding lowest value is:
set lowestValue to null
for each value
if lowestValue == null || value < lowestValue
lowestValue = value
As you have multiple values to look for, each identified by the stock name, you define lowest_value as Map<String, BigDecimal> where the key is the stock identifier. Once you have processed each line in the input data, the map contains the lowest value for each stock. There is no need for nested loops, as everyting is done during the single pass. Once the you have populated the map, you can do the printing in a separate single loop over the map data.
I have a problem statement as described below:
Write a function that takes this input as a parameter and returns a data structure containing the number of
clicks that were recorded on each domain AND each subdomain under it.
For example, a click on "mail.yahoo.com" counts toward the totals for "mail.yahoo.com", "yahoo.com", and "com".
(Subdomains are added to the left of their parent domain. So "mail" and "mail.yahoo" are not valid domains.
Note that "mobile.sports" appears as a separate domain near the bottom of the input.)
Below is the input data:
String[] counts = {
"900,google.com",
"60,mail.yahoo.com",
"10,mobile.sports.yahoo.com",
"40,sports.yahoo.com",
"300,yahoo.com",
"10,stackoverflow.com",
"20,overflow.com",
"5,com.com",
"2,en.wikipedia.org",
"1,m.wikipedia.org",
"1,mobile.sports",
"1,google.co.uk"
};
Below is the expected output:
calculateClicksByDomain(counts) =>
com: 1345
google.com: 900
stackoverflow.com: 10
overflow.com: 20
yahoo.com: 410
mail.yahoo.com: 60
mobile.sports.yahoo.com: 10
sports.yahoo.com: 50
com.com: 5
org: 3
wikipedia.org: 3
en.wikipedia.org: 2
m.wikipedia.org: 1
mobile.sports: 1
sports: 1
uk: 1
co.uk: 1
google.co.uk: 1
I tried to write a solution for above problem statement:
Map<String, Integer> calculateClicksByDomainMap = new HashMap<>();
for(int i = 0; i < counts.length; i++) {
String[] seperateClickCountsAtComma = counts[i].split("\\,");
for(int j = 0; j < seperateClickCountsAtComma.length; j += 2) {
String clickCounts = seperateClickCountsAtComma[j];
String domain = seperateClickCountsAtComma[j+1];
calculateClicksByDomainMap.put(domain, Integer.parseInt(clickCounts));
}
}
for(Entry<String, Integer> domainCounts : calculateClicksByDomainMap.entrySet()) {
String domainName = domainCounts.getKey();
Integer domainCount = domainCounts.getValue();
splitStringOnOccurenceOfDot(domainName);
//System.out.println(domainName + " " + domainCount);
//String test[] = domainName.split("\\.");
//System.out.println(test[0] + "=======" + test[1] + "-----");
}
public static String splitStringOnOccurenceOfDot(String domainName) {
if(!domainName.contains(".")) {
return domainName;
}
String[] subdomain = domainName.split("\\.");
domainName = subdomain[1];
System.out.println(domainName + "===============" );
return splitStringOnOccurenceOfDot(domainName);
}
However, I'm not sure how to split the string using recursion.
Can anyone help me what is the efficient way to write code in order to get the expected output?
I there a way to solve it using recursion?
Thank you for your time.
The Map needs to ConcurrentHashMap, so that it allows concurrent updates.
First for loop with nesting can call the recursive method like below, might not need another for loop:
Map<String, Integer> calculateClicksByDomainMap = new ConcurrentHashMap<>();
for (final String count : counts) {
String[] separateClickCountsAtComma = count.split("\\,");
for (int j = 0; j < separateClickCountsAtComma.length; j += 2) {
Integer clickCount = Integer.parseInt(separateClickCountsAtComma[j]);
String domain = separateClickCountsAtComma[j + 1];
calculateClicksByDomainMap.put(domain, clickCount);
splitStringOnOccurenceOfDot(domain.substring(domain.indexOf(".")+1), clickCount, calculateClicksByDomainMap);
}
}
Rather than returning a value, recursion add/update map itself.
public static void splitStringOnOccurenceOfDot(String domainName, Integer domainCount, Map<String, Integer> calculateClicksByDomainMap) {
if(calculateClicksByDomainMap.containsKey(domainName)) {
Integer newCount = calculateClicksByDomainMap.get(domainName) + domainCount;
calculateClicksByDomainMap.put(domainName, newCount);
} else {
calculateClicksByDomainMap.put(domainName, domainCount);
}
if(domainName.contains(".")) {
splitStringOnOccurenceOfDot(domainName.substring(domainName.indexOf(".")+1), domainCount, calculateClicksByDomainMap);
}
}
I am trying to compare two Strings based on which the boolean dontuse will be set. For practice I have set string as static but they may vary (Please dont ask how I get these Strings it a whole big beast in itself).
What I am trying to do is, check if String 'allofmycode' has any element more then 'codeChoosen' then 'dontuse' is false. But if both of the string are equal then its is false or if the string has same element written several times then also false.
So, for
(1) it will be false as it contains AB & CD which are extra.
(2) will be true as it has the same element but is duplicate.
I hope it make sense, any suggestions or help? Thanks
public static void main(String[] args) {
boolean dontuse = false;
String codeChoosen = "EX,ZX";
String allofmycode = "EX,AB,CD,EX"; //(1) dontuse=false
//String allofmycode = "EX,EX,ZX"; //(2) dontuse =true
List<String> mycodeChoosen = Arrays.asList(codeChoosen.split("\\s*,\\s*"));
System.out.println("Selected : \t " + mycodeChoosen);
List<String> allofmyresult = Arrays.asList(allofmycode.split("\\s*,\\s*"));
System.out.println("All : \t" + allofmyresult);
if (mycodeChoosen.equals(allofmyresult)) {
dontuse = true;
} else {
for (int i = 0; i < mycodeChoosen.size(); i++) {
if (allofmyresult.contains(mycodeChoosen.get(i))) {
System.out.println(mycodeChoosen.get(i));
}
}
}
System.out.println("\n[DONT USE IS] : \t " + dontuse);
}
For example in basic english, If you have Apple, Orange and Banana; and I say people with Apple and Orange are not coming to my party. But you also have Banana so you are allowed to come to the party. But if you had two Apples or Apple and Banana then you are not allowed. I hope it make sense.
dontuse = !allofmyresult.stream().anyMatch(str -> !mycodeChoosen.contains(str));
As the problem seems to require only a view as sets, do:
static Set<String> codes(String list) {
Set<String> set = new HashSet<>();
Collections.addAll(set, list.split("\\s*,\\s*"));
return set;
}
String codeChoosen = "EX,ZX";
String allofmycode = "EX,AB,CD,EX"; //(1) dontuse=false
Set<String> choosen = codes(codeChoosen);
Set<String> all = codes(allofmycode);
boolean dontuse = choosen.equals(all);
// all.containsAll(choosen)
The original code probably was meant to do:
if (mycodeChoosen.equals(allofmyresult)) {
dontuse = true;
} else {
dontuse = true;
for (int i = 0; i < mycodeChoosen.size(); i++) {
if (allofmyresult.contains(mycodeChoosen.get(i))) {
dontuse = false;
break;
}
}
}
I am creating a table from ajax and the getting of values using a while loop:
while (rstdb.next()) {
rstdb.getInt(1)+"~"+ rstdb.getInt(2);
}
In my while loop my rstdb.getInt(1) will be 2,2,2,2,2,3,3...... and second values rstdb.getInt(2) are 10,20,30,40,50,10,20,.....
I need to sum up the values specific to 2 and values specific to 3 seperate.
ie,
It means 10+20+30+40+50 =150 for 2 and 10+20 =30 for 3.
It may contain single values also for example it may have 4,5,5,6,7,7....
How can I do that?
I need something like:
while (rstdb.next()) {
rstdb.getInt(1)+"~"+ rstdb.getInt(2)+"~"+sum;
}
The variable sum should contain the sum up value.
Use map for this. You can have a map which should be mapping the specific number with sum of it's corresponding value.
int c1, c2;
Map<Integer, Integer> sum = new HashMap<>();
while (rstdb.next()) {
c1 = rstdb.getInt(1);
c2 = rstdb.getInt(2);
if(sum.containsKey(c1)) {
sum.put(c1, sum.get(c1) + c2);
// ^ will return current sum of second column
} else {
sum.put(c1, c2);
}
rstdb.getInt(1)+"~"+ rstdb.getInt(2)+"~"+sum.get(c1);
}
You can use an integer to integer map:
Map<Integer, Integer> integerMap = new HashMap<>();
while (rstdb.next()) {
int column1 = rstdb.getInt(1);
int column2 = rstdb.getInt(2);
if (integerMap.containsKey(column1)) {
int currentSum = integerMap.get(column1);
integerMap.put(column1, currentSum + column2);
} else {
integerMap.put(column1, column2);
}
}
Edit: to print out the map, you can use loop through the entrySet of the map:
for (Map.Entry entry : integerMap.entrySet()) {
System.out.println(entry.getKey() + " : " + entry.getValue());
}
I have this following test code:
public static final String[] list = {
"apple","ball","cat","dog","egg","fan","girl","hat","igloo","jerk"
};
...
HashMap<DoubleKey<Integer, Integer>, String> hm = new HashMap<DoubleKey<Integer, Integer>, String>();
Set<DoubleKey<Integer, Integer>> s = new TreeSet<DoubleKey<Integer, Integer>>();
Random g = new Random();
for(int i=0; i<10; i++){
int first = g.nextInt(9999) + 1000;
int second = g.nextInt(9999) + 1000;
DoubleKey<Integer, Integer> k1 = new DoubleKey<Integer, Integer>(first, second);
DoubleKey<Integer, Integer> k2 = new DoubleKey<Integer, Integer>(first, second);
s.add(k1);
hm.put(k2, list[i]);
}
Set<DoubleKey<Integer, Integer>> ts = hm.keySet();
Iterator<DoubleKey<Integer, Integer>> itr = ts.iterator();
while(itr.hasNext()){
DoubleKey<Integer, Integer> k = itr.next();
System.out.println(k.getFirstKey().toString() + " + " + k.getSecondKey().toString() + " -> " + hm.get(k).toString());
}
System.out.println("----");
Iterator<DoubleKey<Integer, Integer>> sItr = s.iterator();
while(sItr.hasNext()){
DoubleKey<Integer, Integer> k = sItr.next();
String currStr = hm.get(k);
System.out.println(k.getFirstKey().toString() + " + " + k.getSecondKey().toString() + " -> " + currStr);
}
What I did is to create a Custom Generic Class DoubleKey<K, J> to contain a key having two parts. As you can see, the Set s and the keys of HashMap hm are have the same components, but was instantiated differently (k1 = k2). When I try to get a value using the keys on s to hm, it returns null, though at the first printing it shows the correct mapping.
Sample Output:
3922 + 2544 -> girl
9267 + 3750 -> hat
3107 + 10929 -> apple
5162 + 8834 -> fan
8786 + 1125 -> cat
10650 + 4078 -> egg
3808 + 7363 -> jerk
1364 + 7657 -> dog
1364 + 4412 -> ball
1583 + 1460 -> igloo
----
10650 + 4078 -> null
1364 + 4412 -> null
1364 + 7657 -> null
1583 + 1460 -> null
3107 + 10929 -> null
3808 + 7363 -> null
3922 + 2544 -> null
5162 + 8834 -> null
8786 + 1125 -> null
9267 + 3750 -> null
This is my DoubleKey implemention:
public class DoubleKey<K extends Comparable<K>,J extends Comparable<J>> implements Comparable<DoubleKey<K,J>>{
private K key1;
private J key2;
public DoubleKey(K key1, J key2){
this.key1 = key1;
this.key2 = key2;
}
public K getFirstKey(){
return this.key1;
}
public J getSecondKey(){
return this.key2;
}
// need for Comparable interface
public int compareTo(DoubleKey<K,J> aThat){
// NOTE: check for nulls
return (this.key1.toString() + this.key2.toString()).compareTo(aThat.key1.toString() + aThat.key2.toString());
}
public boolean equals(DoubleKey<K,J> aThat){
return (this.key1.toString() + this.key2.toString()).equals(aThat.key1.toString() + aThat.key2.toString());
}
}
How did it happened? Can two objecst (in this case from a custom generic) be different eve3n if they have instantiated with 2 same values? How can I correct this? I hope someone can help me here. Thanks!
Additionally to .hashCode(), you should have an implementation of equals(Object), not (only) equals(DoubleKey<...>), since otherwise you'll have two independent methods here (and only the first one is actually called by the HashMap). Here is a proposal:
public boolean equals(Object other) {
if(this == other)
return true;
if(!(other instanceof DoubleKey))
return false;
DoubleKey that = (DoubleKey)other;
return (this.key1 == null ? that.key1 == null : this.key1.equals(that.key1)) &&
(this.key2 == null ? that.key2 == null : this.key2.equals(that.key2));
}
The hashCode method should be made to fit this, too, for example like this:
public int hashCode() {
return key1.hashCode() * 3 + key2.hashCode() * 5;
}
Your key1.toString()+key2.toString() comparison is a bit dangerous, as it lets (1, 21).equals((12,1)) be true, which is usually not intended. The same is true for your compareTo method - compare the components using their compareTo method, not the concatenated String.
Learn this lesson now: If you override the equals method (as you have done), then you MUST override the hashcode method too. That method is used for various things, including looking up items in HashMaps.
Where is DoubleKey class's hashCode method override? I don't think that it will work as a proper key unless you implement this because otherwise your two objects will be considered different.