I have a class with two methods: the startAPI() calls the API classes to extract entities and returns the entities and the occurrence of the entities. I need this return value in two different methods from another class, but as soon as I call the second method (countApiOcc()) the map I pass is empty. How can I use the returned map in two different methods?
public class Topic {
public void calculateNoFeedback(String language, List<String> api, List<String> corr) {
Map<String, Object> apis = startAPI(api, textList);
CountTopics countT = new CountTopics();
ArrayList<String> topics = countT.getTopics(apis);
countT.countApiOcc(topics, apis);
}
public Map<String, Object> startAPI(List<String> selectedAPI, List<String> text) {
Map<String, Object> apisValues = new HashMap<String, Object>();
//do stuff to extract the entities and return entities
return apisValues;
}
}
The CountTopic() class looks as follows, and, explained in short, user can select which or how many APIs he wants to use to extract entities and in the class CountTopic() the method getTopics() should find the topics every selected API found and countApiOcc() I need the frequency of the selected entities (all of this works) it is just the map I need in the second method.
public ArrayList<String> getTopics(Map<String, Object> apiV) {
System.out.println("apiV: "+apiV);
Iterator iterator = apiV.entrySet().iterator();
mapSize = apiV.size();
System.out.println("Size of the map: "+ mapSize);
while (iterator.hasNext()) {
Map.Entry entries = (Map.Entry)iterator.next();
String key = entries.getKey().toString();
switch(key) {
case "valuesMS":
Map<String, Object> mapMicrosoft = (Map<String, Object>) apiV.get(key);
ArrayList<String> microsoft = (ArrayList<String>) mapMicrosoft.get("topicArrayMS");
microsoftTopicLowerCase.addAll(microsoft);
topicsMultiset.addAll(microsoft);
break;
case "valuesGate":
Map<String, Object> mapGate = (Map<String, Object>) apiV.get(key);
ArrayList<String> gate = (ArrayList<String>) mapGate.get("topicArrayGA");
//store the values for finding the topics which are found from every selected API
//store the values from the api to lower case to find the index later (needed for how often this api found the topic
gateTopicLowerCase.addAll(gate);
topicsMultiset.addAll(gate);
break;
}
iterator.remove();
}
//rest code: compare the Arrays to find the same topics
iterator.remove();
There's your culprit. You're emptying your map. Don't do this, and it will work. From my limited view on your code, there doesn't seem to be any reason to modify your map. But in case it would be necessary, you should make a copy of the map at the beginning of your method, and work on this copy. Generally it's a bad idea to modify your input parameters, unless that is the specific purpose of that method.
Related
Is there a way to get or set an array element stored in a Java Map?
Example:
If we have a map like this:
{
name: "Blah",
friends: ["Foo", "Bar"]
}
Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
myMap.put("name", "Blah");
myMap.put("friends", friends);
Is it possible to use Reflection to get or set the first element in the friends array in the "myMap" from the string: "myMap.friends[0]"
Your question is not very clearly written and I believe that's why you are not getting the answer you expect but, If I understood your question correctly, you need to parse the following input string at runtime that you don't know beforehand:
myMap.friends[0]
And this should be parsed into components like:
mapName = "myMap"
mapKey = "friends"
valueIndex = 0
And with this information, you need to manipulate data in a Map at runtime through reflection.
Note: This only makes sense if you could potentially have more complex expressions, using different sort of objects and accessing nested properties of retrieved objects, otherwise you wouldn't need reflection at all.
Note 2: You may want to have a look at JXPath which already does a lot of this for you based on a XPath-like syntax for navigating object graphs.
That said, if my assumptions are correct and you still want to do it yourself, consider the following example.
For the sake of demonstration, let's consider our map is returned by a method myMap inside a Context.
private static class Context {
public Map<String, Object> myMap() {
Map<String, Object> myMap = new HashMap<>();
List<String> friends = new ArrayList<>();
friends.add("Foo");
friends.add("Bar");
myMap.put("name", "Blah");
myMap.put("friends", friends);
return myMap;
}
}
I'm assuming you are already parsing the input string into the different components. If not, for this simple string you could do it with simple regular expressions. If you already have the components, let's consider the following method:
public static Object readContextMap(Context context,
String mapName, String mapKey, Integer mapValueIndex) throws Exception {
// gets Context class for inspection
Class<?> cls = context.getClass();
// search for a method based on supplied mapName
Method mapMethod = cls.getDeclaredMethod(mapName);
// get a value from the retrieved map based on mapKey
Object mapValue = mapMethod.getReturnType()
.getDeclaredMethod("get", Object.class)
.invoke(mapMethod.invoke(context), mapKey);
// if the result is of type list, use the index to return the indexed element
if (List.class.isAssignableFrom(mapValue.getClass())) {
return ((List<?>)mapValue).get(mapValueIndex);
}
// otherwise return the object itself
return mapValue;
}
For testing purposes, consider the following main method:
public static void main(String[] args) throws Exception {
Context context = new Context();
String input = "myMap.friends[0]";
// parse input into...
String mapName = "myMap";
String mapKey = "friends";
Integer valueIndex = 0;
Object firstFriend = readContextMap(context, mapName, mapKey, valueIndex);
System.out.println(firstFriend);
// prints Foo
Object name = readContextMap(context, "myMap", "name", null);
System.out.println(name);
// prints Blah
}
This should be approximately what you want. You can easily create variations of this to set values as well. Please bear in mind that this code is just for demo purposes and needs a better error handling (e.g. verify if the context is really returning a map and nothing else).
This should be something along the lines you are looking for.
There's no need to use reflection here. You can simply cast it (which is also unsafe, but less so).
You can just do this:
List<String> friends = (List<String>) myMap.get("friends");
friends.set(0, "Bob");
I have the below method, in which I am extracting the value from the entity and then setting it in map as a value of that map but my point is that for each key I am setting the value explicitly so if the count of keys grows that method code will also grow , can I make a common method based on approach Map.computeIfPresent, please advise how can I achieve both the things
private void setMap(AbcLoginDTO abcLoginDTO, Map<String, Object> getMap) {
getMap.put("XXNAME", abcLoginDTO.getUsername());
getMap.put("XX_ID", abcLoginDTO.getClientId());
getMap.put("RR_ID", abcLoginDTO.getUserId());
getMap.put("QQ_TIME", abcuserLoginDTO.getLocktime());
}
something like in this below approach I am thinking
static <E> void setIfPresent(Map<String, Object> map, String key, Consumer<E> setter, Function<Object, E> mapper) {
Object value = map.get(key);
if (value != null) {
setter.accept(mapper.apply(value));
}
}
but my point is that for each key I am setting the value explicitly so
if the count of keys grows that method code will also grow
You need to populate the Map with different values from the DTO, so you don't have other choices.
The method is long because you don't have a mapping between the key to add in the Map and the value to retrieve from the DTO.
You could write your code with a function such as :
static void setValueInMap(Map<String, Object> map, String key, Supplier<Object> mapper) {
map.put(key, mapper.get());
}
And use that :
Map<String, Object> map = ...;
AbcLoginDTO dto = ...;
setIfPresent(map, "keyUserName", dto::getUserName);
// and so for
But no real advantages.
Your second snippet has not at all relationship with the first one.
If i understand correctly, what you want to do is iterate over all of the object's members, get their value, and set them to a map according to their name. If so, then what you're looking for is called Reflection.
Every object can give you an array of its fields or methods (even private ones!) and then you can manipulate them using the Field / Method object.
Field[] members = AbcLoginDTO.class.getDeclaredFields();
Map<String, Object> values = new HashMap<>();
for(Field member : members) {
member.setAccessible(true);
values.put(member.getName(), member.get(abcLoginDTO));
}
What you end up with here, is a "Map representation" of your AbcLoginDTO instance. from here you can do with it what you want...
notice that i am "inspecting" the class itself in line 1, and then using the instance at line 6.
this code is not complete, but it's a start, and this can also be adapted to work for ANY object.
I don't know if I understood correctly, but if I did then that means all you need is a way to manually set different keys for the methods of your AbcLoginDTO class
If so then that can be done easily,
let's consider that your abcLoginDTO.getClientId() is always different for every AbcLoginDTO object:
private void setMap(AbcLoginDTO abcLoginDTO, Map<String, Object> getMap) {
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_NAME", abcLoginDTO.getUsername());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_ID", abcLoginDTO.getClientId());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_ID", abcLoginDTO.getUserId());
getMap.put(Integer.toString(abcLoginDTO.getClientId())+"_TIME", abcuserLoginDTO.getLocktime());
}
I want to prepare a HashMap in such way that
Key : Country Code
Value : List of returned orderEntries
the following process data method process every 5 orderEntry which can be from any country.
let me make it more clear. I have list of orderEntries that come from different countries now I want to put these entries into map based on country key. Like if 20 entries coming from US then US will be the key and 20 Entries would be the values. But problem is that I don't want to create a list for each county inside map.
public void processSegmentData(final List resultSet)
{
for (final Object orderEntry : resultSet)
{
if (orderEntry instanceof OrderEntryModel)
{
String countryCode = null;
final OrderModel order = ((OrderEntryModel) orderEntry).getOrder();
if (order.getDeliveryAddress() != null)
{
countryCode = order.getDeliveryAddress().getCountry().getIsocode();
}
orderEntriesMap.put(Config.getParameter(countryCode+".return.pid"), orderEntries);
}
}
}
so you are after a hashmap which contains a linked list Something along the lines of:
public HashMap<String, LinkedList<OrderEntryModel>> processSegmentData(final List resultSet) {
HashMap<String, LinkedList<OrderEntryModel>> orderEntriesMap = new HashMap<String, LinkedList<OrderEntryModel>>();
for (final Object orderEntry : resultSet) {
if (orderEntry instanceof OrderEntryModel) {
String countryCode = null;
final OrderModel order = ((OrderEntryModel) orderEntry).getOrder();
if (order.getDeliveryAddress() != null) {
countryCode = order.getDeliveryAddress().getCountry().getIsocode();
}
if (!orderEntriesMap.containsKey(countryCode)) {
orderEntriesMap.put(countryCode, new LinkedList<OrderEntryModel>());
}
orderEntriesMap.get(countryCode).add((OrderEntryModel) orderEntry);
}
}
return orderEntriesMap;
}
would be an example based on the source code you provided guessing object names.
But problem is that I don't want to create a list for each county
inside map.
I understand your problem but map store unique key, you can not store same country code.
you have to use Map<String, List<String>>() that will hold your country code as key and then put your values inside List<String>.
after doing this if you have any problem edit your question will help you to resolve that.
Just Create a Map<String,List<String>>. and follow the following approach
Map<String,List<String>> countryMap = new HashMap<String, List<String>>();
for (final String orderEntry : orders){
if(countryMap.containsKey(orderEntry.getCountry())){
countryMap.get(orderEntry.getCountry()).add(orderEntry);
}else{
//create a new list and add orderEntry
countryMap.put(orderEntry.getCountry(),orderEntry);
}
}
You need to modify this according to your stuff
You could use Guava's Multimap to simplify things. A Multimap allows you to store multiple entries against a single key, e.g.:
Multimap<String, OrderEntry> orderEntriesMultimap = HashMultimap.create();
for (final Object orderEntry : resultSet) {
// omitted...
orderEntriesMultimap.put(Config.getParameter(countryCode+".return.pid"), orderEntry);
}
You can then retrieve all the associated values by key:
Collection<OrderEntryModel> entries = orderEntriesMultimap.get(key);
So I have a hashmap
HashMap<String, ArrayList<String> gMap = new HashMap<String, ArrayList<String>();
And when someone creates a group, the group leader is added to the key of the hashmap and then all the users inside the group are added to the arraylist
gMap.get(groupLeader).add(user);
I'm trying to make it so that only group leaders are allowed to invite players, but if a player is not part of any group and invites another user then a group is automatically created and the player becomes the group leader.
So, normally I would just do this
for(ArrayList<String> list : gMap.values()){
if(list.contains(user)){
//do something since the player is not part of the list
}
}
But I cannot do that since there could be multiple arrayLists, so even though the user is not part of one arrayList it does not mean that they aren't inside another.
So I'm curios how I would check all the arrayLists and do something only if the user is not part of any of them.
You're actually creating a Multimap (a mapping of keys to collections of values) data structure here, and you'll find if you use one directly you won't have to reinvent the wheel as you are now. Guava defines a very nice Multimap interface, including an ArrayListMultimap that stores the data you want, and has a containsValue() method that cleanly does what you need. As mentioned by others, contains checks against a list are slow, you could use HashMultimap to do these contains checks more efficiently if you don't actually care about order, or LinkedHashMultimap if you really do.
And if you aren't already using Guava, you're missing out - it provides countless excellent utilities and good practices.
Use a boolean value that you only change the value of if a list contains the user - and break out of the loop if it does.
boolean notInAnyList = true;
for(ArrayList<String> list : gMap.values()){
if(list.contains(user)){
notInAnyList = false;
break; // No point in iterating any further
}
}
if (notInAnyList) {
// Handle user not being in any of the lists here
}
So here is a code sample
boolean userIsPartOfGroup = false
for(ArrayList<String> list : gMap.values()){
if(list.contains(user)){
userIsPartOfGroup = true;
break;
}
}
if(!userIsPartOfGroup){
gMap.add(user).add(new ArrayList(user));
}
First, I would use a set instead of a list (Java 7 syntax):
Map<String, Set<String> gMap = new HashMap<>();
When I understand your goal right, this may be a solution:
if (isLeader(groupLeader, gMap)) {
gMap.get(groupLeader).add(user);
} else if (isMember(groupLeader, gMap)) {
throw new UnsupportedOperationException("Member " + groupLeader + " is not a leader and must not invite " + user);
} else {
addNewGroup(groupLeader, gMap).add(user);
}
Here are the helper methods:
private boolean isLeader(String player, Map<String, Set<String> gMap) {
return gMap.keys().contains(player);
}
private boolean isMember(String player, Map<String, Set<String> gMap) {
for (Set<String> members : gMap.values()) {
if (members.contains(player)) {
return true;
}
}
return false;
}
private Set<String> addNewGroup(String leader, Map<String, Set<String> gMap) {
Set<String> players = new HashSet<>();
gmap.put(leader, players);
return players;
}
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Maps with multiple types of values in java
I have an odd question. Maybe I'm going about this the wrong way, but let's see where this question goes :)
I would like a Map container that contains either Strings or lists of Strings. I want to enforce this rule during construction of this object so that you can't create a map with values that aren't either of those.
e.g.
class Record {
public Record(String key, Map<String,Object> attrs) {
// check that attrs only contains Objects which are Strings or List<Strings>
}
}
Other ways I have thought of to solve the problem might be...
1)
class Record {
public Record(String key, Map<String,String> attrs, Map<String,List<String>> multiAttrs) {
// ...
}
}
2)
class Record {
public Record(String key, Map<String,Value> attrs) {
// ...
}
}
class Value {
// Create some funky class that encapsulates lists.
// Perhaps returning the only element in the list if the size is 1,
// but returning the list otherwise
}
I am not immediately excited at the alternatives, but I'm just putting it there as stuff I've already considered. Really I want the distinction between Strings and List to be transparent to the user of the class.
Have you considered ListMultimap? For the single value case the list would only have one element. Multimap allows multiple elements (values) to be mapped to each key. So your method would be:
public Record(String key, ListMultimap<String, String> attrs)...
Also, since your Record seems to be another mapping, consider using Table which allows for two-key mapping.
Check out ArrayListMultimap from Google which will help with this need
You can continue calling put on this map, if you need to get the map in its simplified form you can use this method, or modify it :)
public static Map<Field, String> toSingularMap(ArrayListMultimap<Field, String> map) {
Map<Field, String> singular_map = new HashMap<Field, String>();
if (map != null && !map.isEmpty()) {
Map<Field, Collection<String>> real_map = map.asMap();
for (Iterator<Entry<Field, Collection<String>>> it = real_map
.entrySet().iterator(); it.hasNext();) {
Entry<Field, Collection<String>> entry = it.next();
Field field = entry.getKey();
Collection<String> values = entry.getValue();
String value = null;
if (values != null && !values.isEmpty()) {
ArrayList<String> list = new ArrayList<String>(values);
value = list.get(0);
}
singular_map.put(field, value);
}
}
return singular_map;
}
Or if you do not want to use an extra library, you can create a simple Wrapper class
class Wrap {
String value;
String[] values
}
and have your map use Map<String, Wrap> map, when looping you can then determine either through use of your class methods or just testing, which one of the Wrapper variables are populated
I would use only List<String>. You could maybe add some methods to allow adding a single String and wrap the passed argument using Arrays.asList(...). Using only a single type of objects will reduce the quantity of code to write and avoid many if/else.
Why not create a class
class MyFunkyValue{
private String onlyOneString;
private List<String> stringValues;
public MyFunkyValue(String s){
...
}
public MyFunkyValue(List<String>ls){
...
}
}
and use it like this:
Map<KeyClass,MyFunkyValue> m;