I'm sure this isn't a hugely complex problem but I'm relatively new to java and have been puzzling with this one for a while.
Say I have an array which contains 6 string values, and lets say they are: [Apple, Banana, Banana, Banana, Orange, Orange]. I am looking to build a method that will take this array and return a string, in the case above, as: "Apple, 3×Banana, 2×Orange".
I would greatly appreciate some help with this. I've been trying different techniques to achieve this but fall down on the correct behavior on the first iteration of the array and when to detect a repeated value ect.
Use Map<String, Integer>. Iterate over the array and put strings as Map key and counter as value:
Map<String, Integer> words = HashMap<String, Integer>();
for (String word : words) {
Integer count = words.get(word);
count = count == null ? 0 : count++;
words.put(word, count);
}
Now the words map contains mapping between word and its count.
You can use the TreeMap object where the Fruit name is the key and value is the number of times it has been seen.
You iterate through the array fruit by fruit, using say a foreach loop and update that fruit's value in the TreeMap. At the end you print the key and its value. Since we've used a TreeMap the keys will be in sorted order.
What have you tried ? Create instance of HashMap, where keys are fruits names and values are fruits occurrence. Iterate over your your array, on every iteration, add new fruit to your Map or increment the number of occurrence of existing key.
Then iterate over HashMap, and build your String
If everything is in a single ArrayList and you know the fruit that you are expecting you could use
ArrayList<String> fruits = new ArrayList<>();
//load some fruit code [ .. ]
int orangeOccurrences = Collections.frequency(fruits, "orange");
but Map is definitively the more correct approach.
Related
I'm having some trouble figuring out how to keep track of occurrences between two dependent LinkedLists.
Let me elaborate with an example:
These are the linked lists in question.
They are dependent because each value in first list corresponds to the value in the second list with the same index i. Both lists are always the same length.
{sunny, sunny, rainy, sunny, cloudy, sunny, ...}
{yes, no, no, maybe, yes, no, ...}
What I need is to somehow keep track of the "pairs" of occurrences.
For example:
sunny -> 1 yes, 1 maybe, 2 no
rainy -> 1 no
cloudy -> 1 yes
Note: There doesn't have to be exactly 3 options. There can be more or less. Also the names of the items of the lists aren't known previously.
So yea, I'm wondering which is the best way to go about storing this information as I've hit a dead end.
Any help is appreciated.
You may do that with a Map<String, Map<String, Integer>.
The key of the outer map is the weather (sunny, rainy etc.)
The value is another map which contains each possible value (yes, no, maybe...) and the number of times that value occurs.
Something like this to merge the two lists:
public static Map<String, Map<String, Integer>> count(List<String> weathers, List<String> answers) {
//weathers olds the strings 'sunny', 'rainy', 'sunny'...
//answers old the strings 'yes', 'no'...
//this code assumes both lists have the same size, you can enforce this in a check or throw if not the case
Map<String, Map<String, Integer>> merged = new HashMap<>();
for (int j = 0; j < weathers.size(); j++) {
if (merged.containsKey(weathers.get(j))) {
Map<String, Integer> counts = merged.get(weathers.get(j));
counts.put(answers.get(j), counts.getOrDefault(answers.get(j), 0) + 1);
} else {
Map<String, Integer> newAnswer = new HashMap<>();
newAnswer.put(answer.get(j), 1);
merged.put(weathers.get(j), newAnswer);
}
}
return merged;
}
The logic applied to the code above is that you loop through each occurrency of the list(s) and you check if your map already contain that weather.
If it's the case, you get the already existing map and increase the number for that answer (if the answer is not present yet, you will start from zero)
If it's not the case, you add a new map for that weather where you only have the first answer with count 1.
Sample usage:
Map<String, Map<String, Integer>> resume = count(weathers, answers);
//How many times 'sunny' weather was 'maybe'?
Integer answer1 = resume.get("sunny").get("maybe");
//How many times 'rainy' weather was 'no'?
Integer answer2 = resume.get("rainy").get("no");
//etc.
I have this HashMap containing words and the count for each word from a given text file;
{word=1, word2=1, word3=2, word4=1, word5=1, word6=4, word7=1, word8=1};
i was following your suggestion in other topics; but i have notice that if use fro example sorted Collections and i search for a specific KEY which could be 1 in this case it only return me one word while instead can return more values for same key;
the point is between all the collections:
Lists
Maps
ArrayLists
Trees
HashMaps
HashTables
which is the most advisable to use?
on my Class the user will input an int and that int will correspond to the 1st or 2nd or 3rd or 4th and so on..... words used in the files base on the count and occurences;
it's challenging
so far i have managed to store in hashmap and eventually order it in a Tree by Desc Key; so first element will be the greater; but still the algorithm needs more sense;
ps. i do not expect solution or pieces of codes but a good input to start ... a very good advise or direction best to follow;
Maps, by nature, store (will return) only one element per key. That means that if you'll store [key:1, val:a] and then store again [key:1, val:2]. The second insertion will override the first and when you'll "get" key:1 the returned result will be b.
You can, however, to store a List per key. This list can store all the value values per the same key.
So we'll declare the map that we'l use as follows:
Map<String, LinkedList<String>> map = new HashMap<>();
This is how insert should look like:
public void insert(String key, String value){
List<String> values = map.get(key);
if (values == null){ // this is the first time we use this key
values = new LinkedList<String>(); // so we need to create a new values List
}
values.add(value);
map.put(key, values)
}
the "get" is pretty straightforward:you get the list of values and if it's not null - iterate the values and print/do whatever you want with them.
public List<String> get(String key){
return map.get(key);
}
If I understand you correctly, you have a Map<String,Integer> map; which maps words to their frequencies, and now you want to look up words by frequency.
Create a new map that Map<Integer,List<String>> and fill it using the first map:
Map<Integer,List<String>> reverseMap = new HashMap<>();
for (Map.Entry<String,Integer> entry : map.entrySet()) {
List<String> list = reverseMap.get(entry.getValue());
if (list == null) {
reverseMap.put(entry.getValue(), list = new ArrayList<>());
}
list.add(entry.getKey());
}
You will get a map like:
java.util.HashMap[3] {
1=[word2, word4, word, word5, word7, word8]
2=[word3]
4=[word6]
}
In this map, each key is the frequency, each value is a list of words having that frequency.
Edit: To pull out the words having the second-most used frequency ("rank 2"), generate a list of the most used frequencies:
List<Integer> frequencies = new ArrayList<>(reverseMap.keySet());
Collections.sort(frequencies, Collections.reverseOrder());
Then:
System.out.println(reverseMap.get(frequences.get(2 - 1)));
Or, to sort the entire map in descending order of frequency (4, 2, 1), declare it as a TreeMap instead of a HashMap, with a reverseOrder comparator:
Map<Integer,List<String>> reverseMap = new TreeMap<>(Collections.reverseOrder());
I need to do the following task:
I have a list with items.
Each of the items also have a List with strings like "gkejgueieriug"
Now I need to run throw the list and check how many of the items in the list of each item are also in the current element
here is a small pseudeo code:
OneItem;
List AllItems;
for Item in AllItems:
int count = number strings in Item.Values which are also in OneItem.Values
because the data is very big, I need some help to make a efficent implementation.
How to do this? Should I use a hashmap? how to count the overlap?
Your question doesn't provide detailed information about the involved types which you want to compare. So I assume you have a List<Item>. Each item has a String and an own List<Item>
So first I would create a HashSet of the Strings of the Items in your AllItems-List. Iterate the AllList and add the String of each Item to the HashSet.
Then in the second step iterate the AllList again and iterate the List in the Items and check each String here if it is in the HashSet which was created before.
If you have to check this several times you can keep the HashSet as a cache which you refresh when the AllList gets changed.
// Step 1: Create Set of Strings
Set<String> allStrings = new HashSet<String>();
for (Item item : allList) {
allStrings.add(item.getString());
}
// Step 2: Calculate occurrences
for (Item item : allList) {
for (Item internalItem : item.getItems()) {
if (allStrings.contains(internalItem.getString()) {
// Count one up for this String
// This might be done by replacing the HashSet by a HashMap and use its values for counting
}
}
}
Make Item.Values a Set rather than a List. A decent Set implementation - like a HashSet - will run the contains() operation in constant time. Then iterate over one set and increment a count each time the other set contains the element.
An optimization is to always iterate over the smaller set. That way the counting operation is O(n) where n is the size of the smaller set.
If the comparison is only one way (i.e. only counting strings in one list that are also in another but NOT the other way around) then the best way of doing it would probably be to put both lists in a Set instead:
HashSet firstSet = ...
HashSet secondSet = ...
for(each value in firstSet)
{
if(secondSet.contains(value)
{
// Do what you want with the value.
// Sugestion: Add value to a separate set
// so you can track duplicates etc
}
}
With this code you create an ArrayList of Map with the string values and the number of matches in your OneItem.Values...
ArrayList<Map<String,Integer>> matches=new ArrayList<>();
for (Item i : AllItems) {
Map<String,Integer> map=new HashMap<>();
for(String s:values){
map.put(s,Collections.frequency(OneItem.Values, s));
}
matches.add(map);
}
I am trying to present a simplified version of my requirement here for ease of understanding.
I have this class
public class MyClass {
private byte[] data1;
private byte[] data2;
private long hash1; // Hash value for data1
private long hash2; // Hash value for data2
// getter and setters }
Now I need to search between 2 List instances of this class, find how many hash1's match between the 2 instances and for all matches how many corresponding hash2's match. The 2 list will have about 10 million objects of MyClass.
Now I am planning to iterate over first list and search in the second one. Is there a way I can optimize the search by sorting or ordering in any particular way? Should I sort both list or only 1?
Best solution would be to iterate there is no faster solution than this. You can create Hashmap and take advantage that map does not add same key but then it has its own creation overload
sort only second, iterate over first and do binary search in second, sort O(nlogn) and binary search for n item O(nlogn)
or use hashset for second, iterate over first and search in second, O(n)
If you have to check all the elements, I think you should iterate over the first list and have a Hashmap for the second one as said AmitD.
You just have to correctly override equals and hashcode in your MyClass class. Finally, I will recomend you to use basic types as much as possible. For example, for the first list, instead of a list will be better to use a simple array.
Also, at the beginning you could select which of the two lists is the shorter one (if there's a difference in the size) and iterate over that one.
I think you should create a hashmap for one of the lists (say list1) -
Map<Long, MyClass> map = new HashMap<Long, MyClass>(list1.size());//specify the capacity
//populate map like - put(myClass.getHash1(), myClass) : for each element in the list
Now just iterate through the second list (there is no point in sorting both) -
int hash1MatchCount = 0;
int hash2MatchCount = 0;
for(MyClass myClass : list2) {
MyClass mc = map.get(myClass.getHash1());
if(mc != null) {
hash1MatchCount++;
if(myClass.getHash2() == mc.getHash2) {
hash2MatchCount++;
}
}
}
Note: Assuming that there is no problem regarding hash1 being duplicates.
I've been working on something which takes a stream of characters, forms words, makes an array of the words, then creates a vector which contains each unique words and the number of times it occurs (basically a word counter).
Anyway I've not used Java in a long time, or much programming to be honest and I'm not happy with how this currently looks. The part I have which makes the vector looks ugly to me and I wanted to know if I could make it less messy.
int counter = 1;
Vector<Pair<String, Integer>> finalList = new Vector<Pair<String, Integer>>();
Pair<String, Integer> wordAndCount = new Pair<String, Integer>(wordList.get(1), counter); // wordList contains " " as first word, starting at wordList.get(1) skips it.
for(int i= 1; i<wordList.size();i++){
if(wordAndCount.getLeft().equals(wordList.get(i))){
wordAndCount = new Pair<String, Integer>(wordList.get(i), counter++);
}
else if(!wordAndCount.getLeft().equals(wordList.get(i))){
finalList.add(wordAndCount);
wordAndCount = new Pair<String, Integer>(wordList.get(i), counter=1);
}
}
finalList.add(wordAndCount); //UGLY!!
As a secondary question, this gives me a vector with all the words in alphabetical order (as in the array). I want to have it sorted by occurrence, the alphabetical within that.
Would the best option be:
Iterate down the vector, testing each occurrence int with the one above, using Collections.swap() if it was higher, then checking the next one above (as its now moved up 1) and so on until it's no longer larger than anything above it. Any occurrence of 1 could be skipped.
Iterate down the vector again, testing each element against the first element of the vector and then iterating downwards until the number of occurrences is lower and inserting it above that element. All occurrences of 1 would once again be skipped.
The first method would doing more in terms of iterating over the elements, but the second one requires you to add and remove components of the vector (I think?) so I don't know which is more efficient, or whether its worth considering.
Why not use a Map to solve your problem?
String[] words // your incoming array of words.
Map<String, Integer> wordMap = new HashMap<String, Integer>();
for(String word : words) {
if(!wordMap.containsKey(word))
wordMap.put(word, 1);
else
wordMap.put(word, wordMap.get(word) + 1);
}
Sorting can be done using Java's sorted collections:
SortedMap<Integer, SortedSet<String>> sortedMap = new TreeMap<Integer, SortedSet<String>>();
for(Entry<String, Integer> entry : wordMap.entrySet()) {
if(!sortedMap.containsKey(entry.getValue()))
sortedMap.put(entry.getValue(), new TreeSet<String>());
sortedMap.get(entry.getValue()).add(entry.getKey());
}
Nowadays you should leave the sorting to the language's libraries. They have been proven correct with the years.
Note that the code may use a lot of memory because of all the data structures involved, but that is what we pay for higher level programming (and memory is getting cheaper every second).
I didn't run the code to see that it works, but it does compile (copied it directly from eclipse)
re: sorting, one option is to write a custom Comparator which first examines the number of times each word appears, then (if equal) compares the words alphabetically.
private final class PairComparator implements Comparator<Pair<String, Integer>> {
public int compareTo(<Pair<String, Integer>> p1, <Pair<String, Integer>> p2) {
/* compare by Integer */
/* compare by String, if necessary */
/* return a negative number, a positive number, or 0 as appropriate */
}
}
You'd then sort finalList by calling Collections.sort(finalList, new PairComparator());
How about using google guava library?
Multiset<String> multiset = HashMultiset.create();
for (String word : words) {
multiset.add(word);
}
int countFoo = multiset.count("foo");
From their javadocs:
A collection that supports order-independent equality, like Set, but may have duplicate elements. A multiset is also sometimes called a bag.
Simple enough?