Inverse get() for a Map

L

Larry Coon

Is there a convenient & clever way to get a key
from a Map given a value (as opposed to the usual
getting a value given a key)?

I'm using a TreeMap, where the keys are Dates and the
values are Strings which represent the dates. I have
a JComboBox with the Strings. The JComboBox needs to
display the Strings in Date order, which is why I'm
using the Dates as the keys in the TreeMap -- the
TreeMap orders on the keys.

Here's a short example to illustrate what I'm doing.
Where the println() is printing "???" I want the key
associated with the selected item. Is there a better
way than iterating through the entire Map?

import java.awt.*;
import java.awt.event.*;
import java.text.*;
import java.util.*;
import javax.swing.*;

public class TreeMapTest extends JFrame
implements ActionListener {

private JComboBox comboBox;

public TreeMapTest() {
super("TreeMapTest");

Container container = getContentPane();
container.setLayout(new GridLayout(2, 1, 0, 10));

Map map = buildMap();

comboBox = new JComboBox();
comboBox.setModel(new DefaultComboBoxModel(map.values().toArray()));
container.add(comboBox);

JButton goButton = new JButton("Go");
goButton.addActionListener(this);
container.add(goButton);

pack();
setDefaultCloseOperation(EXIT_ON_CLOSE);
setVisible(true);
}

public void actionPerformed(ActionEvent e) {
String selectedValue = comboBox.getSelectedItem().toString();
System.out.println("Selected value = " + selectedValue);

// Here is where I want to get the key for the selected value.
System.out.println("Selected key = ???");
}

private Map buildMap() {
Map result = new TreeMap();
SimpleDateFormat df =
new SimpleDateFormat("MM/dd/yyyy", Locale.getDefault());

try {
result.put(df.parse("10/1/2003"), "Fall '03");
result.put(df.parse("4/1/2004"), "Spring '04");
result.put(df.parse("10/1/2004"), "Fall '04");
result.put(df.parse("4/1/2005"), "Spring '05");
}
catch (ParseException e) {
e.printStackTrace();
}

return result;
}

public static void main(String[] args) {
new TreeMapTest();
}
}
 
T

Tor Iver Wilhelmsen

Larry Coon said:
Is there a convenient & clever way to get a key
from a Map given a value (as opposed to the usual
getting a value given a key)?

Yes, use a second Map with the reverse relationship in it.

public class ReverseMap extends HashMap {
private HashMap reverse = new HashMap();
public Object put(Object key, Object value) {
Object ret = super.put(key, value);
reverse.put(value, key);
return ret;
}
// putAll() left as an exercise etc.
public Object getReverse(Object value) {
return reverse.get(value);
}
}
 
F

Filip Larsen

Larry Coon wrote
Is there a convenient & clever way to get a key
from a Map given a value (as opposed to the usual
getting a value given a key)?

I'm using a TreeMap, where the keys are Dates and the
values are Strings which represent the dates. I have
a JComboBox with the Strings. The JComboBox needs to
display the Strings in Date order, which is why I'm
using the Dates as the keys in the TreeMap -- the
TreeMap orders on the keys.

If you only use the TreeMap to sort the dates for the combo box you may
want to consider to skip the map altogether and combine the date value
and its textual representation into one object, something along the
following lines:

import java.util.Date;

public class MyDate implements Comparable {
public MyDate(Date date, String text) {
this.date = date;
this.text = text;
}
public Date getDate() {
return date;
}
public String toString() {
return text;
}
public int compareTo(Object o) {
return getDate().compareTo(((MyDate)o).getDate());
}
final private Date date;
final private String text;
}

Then you can put MyDate objects into a List like ArrayList, sort the
list with Collections.sort and then give the sorted list to the
combobox. When you need the date selected on the combobox you just use
MyDate.getDate on the selected object. If you have more than one sort
order then you may want to move the compare method into a Comparator
class instead.


Regards,
 
C

Cristiano Sadun

Larry Coon wrote

Compose key and value:

class ObjectWithKey {

private Object object;
private Object key;

public ObjectWithKey(Object key, Object object) { ..assign as obvious.. }

public int hashCode() { return object.hashCode(); }
public boolean equals(Object obj) {
return obj instanceof ObjectWithKey ?
object.equals(((ObjectWithKey)obj).object) :
object.equals(obj); // or "false" if u want strict equivalence
}

}

(didnt code for the case when object == null but it's fairly obvious)
 
L

Larry Coon

Filip said:
If you only use the TreeMap to sort the dates for the combo box you may
want to consider to skip the map altogether and combine the date value
and its textual representation into one object, something along the
following lines:

(snipped)

Thanks for the suggestion -- I was stuck thinking about
it as a map, and hadn't even considered alternatives
like this.

But I'll have to think about it more seriously before
coding it your way. The example I gave was for one of
several Maps this app is using which map display values
to database values (to go into the "where" clause in
a SQL select call via JDBC). In every case except this
one I want the data sorted by the display value (the
value that goes into the combo box). It's just this one
oddball case where the data should be sorted by the
database value. This makes Tor's idea of a second,
reversed Map appealing -- in this one case I'd send the
reversed map to the combo box, and the rest of the
application logic would use the selected value in the
combo box as the key to the Map (the non-reversed map in
this case) where it does a map.get(key) to retrieve the
value to stick in the SQL where clause.


Larry Coon
University of California
 
L

Larry Coon

Tor said:
Yes, use a second Map with the reverse relationship in it.

Thanks. This makes sense for what I'm doing --
this is one of several Maps where one value goes
into a combo box and the other value is used for
database lookups. This one is the only oddball,
where the combo box values are sorted by the
database values. Using two maps for this one
case lets me encapsulate all the oddball behavior
into a single method, so the front-end (UI) and
back-end (database logic) can continue to be
generic.


Larry Coon
University of California
 
J

Jesper Nordenberg

Larry Coon said:
Is there a convenient & clever way to get a key
from a Map given a value (as opposed to the usual
getting a value given a key)?

If you don't want to use a reverse map, the only way to do it is to
get the entrySet() and iterate over that until you find your key.

/Jesper Nordenberg
 
L

Larry Coon

Filip said:
If you only use the TreeMap to sort the dates for the combo box you may
want to consider to skip the map altogether and combine the date value
and its textual representation into one object, something along the
following lines:

(code snipped)
Then you can put MyDate objects into a List like ArrayList, sort the
list with Collections.sort and then give the sorted list to the
combobox.

FOLLOW-UP: Many days and several refactorings later, this
is exactly what I ended up doing, and it works great.
Better than using Collections.sort(), the "order by" clause
in my SQL select statement determines the ordering for the
items in the combo box. I now have four distinct data types
(with a common ancestor, so no duplicate code) feeding four
combo boxes this way, and it all works great. Thanks!
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,994
Messages
2,570,222
Members
46,810
Latest member
Kassie0918

Latest Threads

Top