I have an array list which holds a bunch of activities inputted by the user, each of them have a title which is inputted by the user. I want the title to be parsed and inputted into a hashmap. Now the user can perform a search, where they can type certain keywords. I need to use a hashmap to check which activity in the array list they are referring too.
The problem i am having is that i know we cant have more than one values assosiated with a particular key. For example if the title for activity one is : football game, i want football to = 1 and game to = 1 (which indicates its related to the first activity) so that when the user types in one of those words it will pull up this activity.
You could potentially use a hash map on a list of strings. Make your key be a unique integer and then store any words associated with that integer in the string list. That way "game" could be associated with "soccer game" or "hockey game". But it depends how you associate your strings with your keys I guess.
Map<From, To> looks like it can only map to single things. That is luckily not true since To can also be a List<To> or a Set<To> or even a Map again.
Using complex types as values has some downsides though. You have to create those sets/lists per entry and handle null values.
roughly like
private Map<String, List<Integer>> activityMap = new HashMap<>();
private void add(String key, Integer value) {
List<Integer> list = activityMap.get(key);
if (list == null) {
// must create and add the list
list = new ArrayList<>();
activityMap.put(key, list);
}
list.add(value);
}
private List<Integer> getAll(String key) {
List<Integer> list = activityMap.get(key);
// simpler to use if there is never null as result.
if (list == null)
list = new ArrayList<>();
return list;
}
private void remove(String key, Integer value) {
List<Integer> list = activityMap.get(key);
if (list == null)
return;
// here should probably be list.remove(Object) - it looks confusing with Integer though
for (Iterator<Integer> iterator = list.iterator(); iterator.hasNext();) {
Integer listValue = iterator.next();
if (listValue.equals(value)) {
iterator.remove();
}
}
}
Guava Multimap is an implementation of such a structure in case libraries are an option.
I am not sure what is really want to put into hashmap, and I just assume it looks like this:
"user1" => "bascketbal = 1, footdball = 2"
"user2" => "football = 3"
"user3" => "pingpong = 1"
If so, you can use Map<String, Map<String, Integer>>, e.g:
Map userActiveData = new HashMap<String, Map<String, Integer>>();
//For each user, maybe in a loop
Map<String, Integer> active = new HashMap<String, Integer>();
active.put("football", 1);
active.put("game", 2);
userActiveData.put("user1", active);
Use a list as the value of the HashMap.
Map<String, List<String>> map = new HashMap<String, List<String>>();
Pseudo code:
Parse your title and find the keywords.
If the keyword is a new one Then
add it to the HashMap as a key, and add the Activity_id as the first element of the list.
Else (keyword is already in the HashMap) Then
add Activity_id as the next element of the corresponding list.
When you search a keyword you can return the list with all the Activity_ids that matches the keyword
Example:
Input: 1 - soccer game | 2 - badminton game
This is what HashMap will look like
~KEY~ | ~VALUES(List)~
soccer | 1
badminton | 2
game | 1,2
I think this is what you want, it supports the whole key and a only part of the activity to search, please check the output :
public class ActivityManager {
Map<String, Set<String>> map = new HashMap<String, Set<String>>();
public static void main(String[] args) {
ActivityManager testMap = new ActivityManager();
testMap.addActivity("football game");
testMap.addActivity("basketball game");
Set<String> football=testMap.getActivities("football");
Set<String> basketball=testMap.getActivities("basketball");
Set<String> game=testMap.getActivities("game");
Set<String> footballGame=testMap.getActivities("football game");
System.out.println("-------football------");
printActivities(football);
System.out.println("-------game------");
printActivities(game);
System.out.println("-------basketball------");
printActivities(basketball);
System.out.println("-------football game------");
printActivities(footballGame);
}
public void addActivity(String activity) {
String[] keyWords = activity.split(" ");// split key words, change to what you want if needed
Set<String> activities=null;
for (String key : keyWords) {
activities = map.get(key);
if (activities == null) {// do not have any activities mapped to this key yet
activities = new HashSet<String>();
map.put(key, activities);
}
activities.add(activity);// put new value into it.
}
if (keyWords.length>1){
Set<String> activitiesUsingWholeKey =map.get(activity);//get the activities using the whole word
if(activitiesUsingWholeKey==null){
activitiesUsingWholeKey=new HashSet<String>();
map.put(activity, activitiesUsingWholeKey);
}
activitiesUsingWholeKey.add(activity);
}
}
public Set<String> getActivities(String key){
return this.map.get(key);
}
private static void printActivities(Set<String> activities){
for(String activity:activities)
System.out.println(activity);
}
}
Output :
-------football------
football game
-------game------
basketball game
football game
-------basketball------
basketball game
-------football game------
football game
Related
I am currently learning sets and maps through university (still using Java 7).
They have given us a half finished to-do list app to complete. Currently the to-do list takes three String local variables to allow the user to state a job (aJob), a time to do it (aTime) and a date to do it (aDate).
The app also has an instance variable (today) that holds todays date.
I need to come up with a way to check the HashMap for any tasks that are due today. So I need to be able to query just the HashMap values attributed by the aDate local variable.
I know that to iterate Maps that I can place the keys or the values into a Set and then iterate over the set - not a problem. But if I use the values() method (within the Map class) to put these into a set - it places all three Strings per key into the set. I just want to move the aDate values into a set.
Any ideas?
I only seem to be able to find examples where the Maps have just a single Key and Single Value. This list has a single key and three values per key.
Any pointers would be good?
Kind Regards
Edit.....
Just thought I would add some code to help as there have been several different approaches - which I am all very greatful for. But not sure if they suit my needs....
The Job Class is constructed as such...
public Job(String aJob, String aDate, String aTime)
{
Job = aJob;
date = aDate;
time = aTime;
}
I then create the map within the instance declarations for the To Do List class....
Map<Integer, Job> toDoList = new HashMap<>();
So I need to know the best way to iterate over this map, but it is only the Job attribute 'aDate' that is possibly going to hold the value I am after.
Not sure if that helps at all?
Kind Regards
If really the only structure you're allowed to use is a Map where each key has 3 values (which is the case if I understand correctly), of which only one is a Date, you technically could do the following:
map.values()
.stream()
.filter(Date.class::isInstance)
...whatever else you want to do
The other suggested solutions are far better though, design wise.
If you can't use a custom class, as suggested by Toisen, maybe HashMap<String, HashMap<String, ArrayList<String>>> could do the trick for you.
I've added a sample of how to use it (as well as populating it with some random data)
public class FunkyMap {
private HashMap<String, HashMap<String, ArrayList<String>>> jobs;
// For random data
private String[] job = {"EAT", "SLEEP", "FART", "RELAX", "WORK"};
private String[] time = {"MORNING", "BEFORENOON", "NOON", "AFTERNOON", "EVENING", "MIDNIGHT"};
private String[] date = {"FIRST", "SECOND", "THIRD", "FOURTH"};
public FunkyMap() {
jobs = new HashMap<>();
// To populate some random data
Random r = new Random();
for(int i = 0; i < 20; i++) {
String d = date[r.nextInt(date.length)];
if(jobs.containsKey(d)) {
HashMap<String, ArrayList<String>> inner = jobs.get(d);
String t = time[r.nextInt(time.length)];
if(inner.containsKey(t)) {
inner.get(t).add(job[r.nextInt(job.length)]);
} else {
List<String> s = Arrays.asList(new String(job[r.nextInt(job.length)]));
inner.put(t, new ArrayList<String>(s));
}
} else {
jobs.put(d, new HashMap<String, ArrayList<String>>());
}
}
// Actual iteration over date => time => jobs
Iterator<String> i = jobs.keySet().iterator();
while(i.hasNext()) {
String iKey = i.next();
HashMap<String, ArrayList<String>> inner = jobs.get(iKey);
System.out.println("Jobs scheduled for " + iKey);
Iterator<String> j = inner.keySet().iterator();
while(j.hasNext()) {
String jKey = j.next();
ArrayList<String> actualJobs = inner.get(jKey);
System.out.println("\tAt " + jKey);
for(String s : actualJobs) {
System.out.println("\t\tDo " + s);
}
}
}
}
public static void main(String[] args) {
new FunkyMap();
}
}
I took the liberty to assume that dates were unique, and time was unique per date, while a time could hold any number of jobs including duplicates. If the last assumption with jobs is not true, you could swap ArrayList<String> with Set<String>.
Just create a class that holds all data that you need. E.g.
If you need something strange like Map<String, Tuple<String, Integer, Date>> just make a new class that holds the Tuple:
class TupleHolder {
private String firstValue;
private Integer secondValue;
private Date thirdValue;
// get/set here...
}
and use it: Map<String, TupleHolder>
My program uses two HashmMap and they have exactly the same number of entries and the same keys.
One (tableMap) is static and never changes. The other one is dynamic (partitionMap), this means that I need to update values.
My algorithm got a problem, because seems to be adding one more entry when it is supposed to be not.
//I have a LinkedList of strings that I want to add to the HashMap partitionMap
LinkedList<String> partition = new LinkedList<String>();
for (TerminalNode terminalNode : ctx.U()) {
partition.add(terminalNode.getText());
}
//for each entry of tableMap
for(Entry<String, LinkedList<String>> entry : tableMap.entrySet())
{
//I retrieve keys and values from tableMap
String key = entry.getKey();
LinkedList<String> attributes = entry.getValue();
//the condition: if my linkedlist is included in the other do...
if(attributes.containsAll(partition))
{
//get the list of values
ArrayList<LinkedList<String>> l = partitionMap.get(key);
//but the first time is always null since I init partitionMap without values
if(l==null)
{
ArrayList<LinkedList<String>> firstLL = new ArrayList<LinkedList<String>>();
firstLL.add(partition);
partitionMap.put(key, firstLL); //BUG HERE! add one more entry instead of just updating values
}
else
{
l.add(partition);
partitionMap.put(key, l);
}
}
}
Does anybody have an idea why this is wrong?
I just had an issue that I couldn't solve and I would like your opinion.
I have reached at a stage after reading some input files where I have an Array List of objects which includes a number and a color and from the other side I have a HashMap which includes these numbers with names associated.What I want to do is compare the numbers in these two collections and in the end group them by name.That's what I have at the moment and the output I want to succeed.
INPUT
HashMap names
1-Bill
2–John
3-Jason
4-Jack
5-Michael
6-Chris
ArrayList numbers
2-red
3-yellow
1-green
2-pink
2-gold
1-pink
4-brown
DESIRED OUTPUT
Bill [ green , pink ]
John[ red , pink , gold ]
Jason[ blue , red ]
Jack [ brown ]
I have written this code:
public Map<String,String> getAllDetails(){
HashMap<String , String> theEnd =new HashMap<String ,String>();
for (Numbers t : numbers ) {
String plate = t.getNumber();
for (Map.Entry<String, String> entry : names.entrySet()) {
String key = entry.getKey();
if(plate.equals(key)) {
theEnd.put( t.getName() ,entry.getValue());
}
}
}
return theEnd;
}
And the result I get is Pink = Bill , gold = John , red = Jason ,brown=Jack
So for each color I get a name instead of getting all the colors for each name.
What can I do to get all the colors and group them by name??
Thank you very much in advance.
I think you should use a Map<String,String[]> or Map<String,ArrayList<String>> and for each name (String) you create a list (ArrayList<String> or String[]) and you add the colors to the created list.
Your program has logically problem. Do change like
Convert map value as List like HashMap<String , List<String>> theEnd =new HashMap<String ,List<String>>
First loop should be on names, than on numbers. You need to check for each person has key i.e.1 significant how may color. (numbers).
Add inner number object values into List<String>
After complete looping, do looping on theEnd map and desplay values.
Finally theEnd has value each member respectively colors.
You should try to find names for your classes/variables/methods that means something for the domain at hand. For instance theEnd is not really explicit. Technically, the method getAllDetails does a join of 2 collections on the "plate number" key.
Anyway, here is a commented solution to your problem :
As I understand, you have some class Number that contains a "plate" number and a color. Depending on what you intend to do with this bean class, you should complete with equals and hashcode methods.
public class Number {
String plate;
String color;
public Number(String plate, String color) {
this.plate = plate;
this.color = color;
}
public String getPlate() {
return plate;
}
public String getColor() {
return color;
}
}
I suppose numbers and names are collections declared in global scope for getAllDetails.
List<Number> numbers = new ArrayList<>();
Map<String, String> names = new HashMap<>();
The result of getAllDEtails is a Map whose key is a String that holds the name, and the value is a Set of String to hold the colors.
public static Map<String, Set<String>> getAllDetails()
{
// No duplicate of color for each name => use a Set
Map<String , Set<String>> theEnd = new HashMap<>();
for (Number t : numbers) {
String plate = t.getPlate();
// find name associated with plate
String name = names.get(plate);
if (name != null) {
// find if the corresponding name exists in result map
Set<String> colors = theEnd.get(name);
// If the name is not already there create the Set and put the entry
if (colors == null) {
colors = new HashSet<>();
theEnd.put(name, colors);
}
// Add the color to the Set
colors.add(t.getColor());
}
}
return theEnd;
}
And you can test this with :
public static void main(String [] args) {
numbers.add(new Number("2", "red"));
numbers.add(new Number("3", "blue"));
numbers.add(new Number("2", "gold"));
numbers.add(new Number("1000", "black"));
names.put("2", "Joe");
names.put("3", "Mickael");
System.out.println(getAllDetails());
}
It gives : {Joe=[red, gold], Mickael=[blue]}
I'm changing your map and list names just to make it easier to read, although your Numbers class is little clueless because you call the property as name but you actually mean a color name, I'm not changing that now but I suggest you change that as well :)
If you're on JDK7 or above, you can use diamond operator skipping type definition on right hand side, something like this
Map<String,String> namesMap = new HashMap<>();
List<Numbers> numbers = new ArrayList<>();
coming to your getAllDetails(), the problem with your code is that your Map values are of type String but you actually want to capture a list of color strings, so change your theEnd map like below.
Map<String , List<String>> theEnd =new HashMap<>();
/* As you want a map( name, list(colors) ), start iterating on map
so that it's easy to read your logic. Also iterate on keySet
of numbers rather than on entries */
for(String num : namesMap.keySet()){
// get the person name for a number
String name = namesMap.get(num);
for(Numbers number : numbers){
// get the number from each Numbers object
String plate = number.getNumber();
if(plate.equals(num)){
// if numbers match get the colorsList by name
List<String> colorList = theEnd.get(name);
/*first time you find a number, colorsList
will be null, so initialize it to empty list*/
if( colorList == null) {
colorList = new ArrayList<String>();
}
colorList.add(number.getName());
/* after adding your color to colorsList,
update the entry in map again */
theEnd.put(name, colorList);
}
}
}
// Finally print your values
for(String person : theEnd.keySet()){
System.out.println(person+"->"+theEnd.get(person));
}
With this, for your input, I got the following output
Bill->[green, pink]
John->[red, pink, gold]
Jason->[yellow]
Jack->[brown]
I am currently working on a little "Hangman" project. I want to make it possible for the user to choose from different categories, such as "Countries" or "Food" and this got me thinking about what would be the best way to handle and sort those categories.
I saved the words to a little text file, which looks something like this:
Countries: Hungary Austria Argentina Canada;
Food: Donut Bread Hamburger;
For now, I created a multidimensional ArrayList that stores all the words, each category in an individual ArrayList in that ArrayList.
ArrayList< ArrayList<String> > words = new ArrayList< ArrayList<String> >();
// ... read words from .txt file and store it in the words-ArrayList ...
I know, that in each category the first word is the title of a category, so if I wanted to get all the titles of the categories, it would look something like this:
for( ArrayList list : words ) {
System.out.println( list.get(0) );
}
Now this method I'm using works perfectally fine, it just seems a bit too complex to me and I was wondering, if there are simpler methods to do that. I want to thank in advance for any suggestions you can give me.
Better to use Map<String, List<String>> for my money. The Map can be a HashMap, and the word category would be the key while the List (an ArrayList in the concrete form) would be the related value.
Then to extract the categories, all you'd need to do would be to extract the key set and iterate through it. e.g.,
Map<String, List<String>> mapList = new HashMap<String, List<String>>();
// fill map here...
for (String key : mapList.keySet()) {
List<String> list = mapList.get(key);
System.out.printf("%s: %s%n", key, list);
}
If you want the keys to be in a certain order, then you'd need to use one of the other concrete implementations of Map such as a TreeMap.
For a simple example:
import java.io.InputStream;
import java.util.*;
public class MapList {
public static void main(String[] args) {
Map<String, List<String>> mapList = new HashMap<String, List<String>>();
String sourcePath = "MapListData.txt";
InputStream source = MapList.class.getResourceAsStream(sourcePath);
if (source == null) {
return;
}
Scanner scan = new Scanner(source);
while (scan.hasNextLine()) {
String line = scan.nextLine().trim();
if (!line.isEmpty()) {
line = line.replace(";", "");
String[] mainTokens = line.split("\\s*:\\s*");
if (mainTokens.length == 2) {
String key = mainTokens[0];
List<String> list = new ArrayList<String>();
String[] subTokens = mainTokens[1].split("\\s+");
for (String subToken : subTokens) {
list.add(subToken);
}
mapList.put(key, list);
}
}
}
if (scan != null) {
scan.close();
}
for (String key : mapList.keySet()) {
List<String> list = mapList.get(key);
System.out.printf("%s: %s%n", key, list);
}
}
}
For me returns:
Beer: [Pilsner, Weiss, Brown_Ale, IPA]
Countries: [Hungary, Austria, Argentina, Canada]
Food: [Donut, Bread, Hamburger]
I have been working with Maps at present and I am baffled by how I can get my program to work effectively. I can iterate over the map get the keys and values and sort them in alphabetical and reverse alphbetical order quite easily and have used custom comparators for this. However, I am now trying to sort the map based on the key with the most values. The values are a list of objects I have created and can be thought of as this scenario.
There is an Atlas(like a catalog) that has lots of towns (the key of type string). That contains Shops(List). I want to sort this so that the town with the most shops is displayed first and goes in descending order with the secondary sorting being based on town alphabetically and return a string representing this.
I have used the Comparator interface with seperate classes for each one alphabetically and reverse alphabetically so far and wish to follow the same pattern for learning purposes However this has me completely stumped.
Example:
class Atlas {
Map<String, List<Shop> atlas = new HashMap<String, List<Shop>();
void addShop(Shop shop){
//if(Atlas already contains){
get the town and add the shop to it.
}
else{
add the town as the key and the shop as the value in the list
}
}
List<Shop> getAllShopsFromTheGivenTown(String givenTown){
//if(Atlas contains givenTown){
return the givenTown from the List.
}
else{
//Return an ArrayList emptyList
}
}
public String returnAllTownsAndShopsAlphbetically(){
String tmpString = "";
List<String> keys = new LinkedList<String>(atlas.keySet());
TownComparatorAtoZ tc = new TownComparatorAtoZ();
Collections.sort(keys, tc);
for(String town : keys){
List<Shop> shops = new LinkedList<Dealer>(atlas.get(town));
ShopComparatorAtoZ sc = new ShopComparatorAtoZ();
Collections.sort(shop, sc);
for(Shop shop : shops){
if(tmpString.isEmpty()){
tmpString = tmpString + town + ": " + shop.getName();
}
else if(tmpString.contains(town)){
tmpString = tmpString + ", " + shop.getName();
}
else{
tmpString = tmpString + " | " + town + ": " + shop.getName(); }
}
}
return tmpString;
}
}
As can be seen from above (although not the cleanest and most efficient) returns things alphabetically and will be reformatted into a string builder. However, I am wondering how I can use a comparator to achieve what I am after and if someone could provide a code snippet with an explanation of what it actually does I would be grateful as its more about understanding how to do it not just getting a copy and pasted lump of code but need to see if visually in code to understand it.
SO output I want to be something like
manchester: m&s, h&m, schuch | birmingham: game, body shop | liverpool: sports
You can try something like this:
public static Map<String, List<Shop>> mySortedMap(final Map<String, List<Shop>> orig)
{
final Comparator<String> c = new Comparator<String>()
{
#Override
public int compare(final String o1, final String o2)
{
// Compare the size of the lists. If they are the same, compare
// the keys themsevles.
final int sizeCompare = orig.get(o1).size() - orig.get(o2).size();
return sizeCompare != 0 ? sizeCompare : o1.compareTo(o2);
}
}
final Map<String, List<Shop>> ret = new TreeMap<String, List<Shop>>(c);
ret.putAll(orig);
return ret;
}
Explanation: TreeMap is the basic implementation of a SortedMap, and it can take a comparator of key values as an argument (if no comparator is passed as an argument, natural ordering of the keys prevails). Here we create an ad hoc comparator comparing the list sizes of the original map passed as an argument, and if the sizes are equal, it compares the keys themselves. Finally, we inject all elements from the origin map into it, and return it.
What if you try something like the following:
private static final Comparator<Map.Entry<String, List<Shop>>> CountThenAtoZ =
new Comparator<Map.Entry<String, List<Shop>>>() {
#Override
public int compare(Map.Entry<String, List<Shop>> x, Map.Entry<String, List<Shop>> y) {
// Compare shop count first. If equal, compare keys alphabetically.
int cmp = ((Integer)x.getValue().size()).compareTo(y.getValue().size());
return cmp != 0 ? cmp : x.getKey().compareTo(y.getKey());
}
};
...
public String returnAllTownsAndShopsAlphbetically() {
List<Map.Entry<String, List<Shop>>> entries = new ArrayList<>(atlas.entrySet());
Collections.sort(entries, CountThenAtoZ);
String result = "";
boolean firstTown = true;
for (Map.Entry<String, List<Shop>> entry : entries) {
if (!firstTown) result += " | "; else firstTown = false;
result += entry.getKey() + ": ";
boolean firstShop = true;
TreeSet<Shop> sortedShops = new TreeSet<>(new ShopComparatorAtoZ());
sortedShops.addAll(entry.getValue());
for (Shop shop : sortedShops) {
if (!firstShop) result += ", "; else firstShop = false;
result += shop.getName();
}
}
return result;
}
The way this works is to first create a list of the atlas entries in exactly the order we want. We need access to both the keys and their associated values to build the correct ordering, so sorting a List of Map.Entry instances is the most convenient.
We then walk the sorted list to build the resulting String, making sure to sort the shops alphabetically before adding them to the String.