I'm seeing some odd behavior in the javax.scripting map implementation.
The online examples show an example of adding to a list within the js environment:
ScriptEngineManager mgr = new ScriptEngineManager();
ScriptEngine jsEngine = mgr.getEngineByName("JavaScript");
List<String> namesList = new ArrayList<String>();
namesList.add("Jill");
namesList.add("Bob");
namesList.add("Laureen");
namesList.add("Ed");
jsEngine.put("namesListKey", namesList);
System.out.println("Executing in script environment...");
try
{
jsEngine.eval("var names = namesListKey.toArray();" + "for(x in names) {" + " println(names[x]);" + "}"
+ "namesListKey.add(\"Dana\");");
} catch (ScriptException ex)
{
ex.printStackTrace();
}
System.out.println(namesList);
However, if you try to do something similar with a map, you see odd behavior. For one thing, if you try to iterate through the map keys, e.g.
HashMap<String, Object> m = new HashMap<String, Object>();
jsEngine.put("map", m);
There's no way to obtain the map keys - if you try to iterate through the keys, you get method names-
jsEngine.eval(" for (var k in m.keySet()){ println(k)};");
results in :
notifyAll
removeAll
containsAll
contains
empty
equals
...
In the js context you can address values in the map with m.get(key) but not with m[key], and if the key doesn't exist it throws an error. Can anyone shed some light on this behavior, or is it just broken? Thanks.
for..in in JavaScript is not the same thing as for..each in Java, even though they look similar. for..in in JavaScript iterates over the property names in an object. The method names are exposed to Rhino as properties on the native Java HashMap object, just as if you had the following JavaScript object:
{
notifyAll:function(){},
removeAll:function(){},
containsAll:function(){},
contains:function(){},
empty:function(){},
equals:function(){}
}
My recommendation is that you either convert the HashMap keyset to an array, using method Set.toArray, or you obtain an iterator using Set.iterator(). Here's a short Rhino script showing how you may accomplish this using the toArray method:
x=new java.util.HashMap();
x.put("foo","bar");
x.put("bat","bif");
x.put("barf","boo");
var keyArray = x.keySet().toArray();
for(var i=0, l = keyArray.length; i < l; i++){
var key = keyArray[i];
var value = x.get(key);
print(value);
}
Which outputs:
bif
bar
boo
Here's how you can do the same thing using Set.iterator:
x=new java.util.HashMap();
x.put("foo","bar");
x.put("bat","bif");
x.put("barf","boo");
var keyIter = x.keySet().iterator();
while(keyIter.hasNext()){
var key = keyIter.next()
var value = x.get(key);
print(value);
}
If you convert java.util.Map to a native object, your JavaScript will be cleaner:
final Map<String,String> javaMap = new HashMap<>();
javaMap.put("alpha", "bravo");
javaMap.put("charlie", "delta");
final NativeObject jsMap = new NativeObject();
for (Map.Entry<String,String> entry : javaMap.entrySet()) {
jsMap.defineProperty(
entry.getKey(), entry.getValue(), NativeObject.READONLY
);
}
final ScriptEngine jsEngine =
(new ScriptEngineManager()).getEngineByName("JavaScript");
jsEngine.put("map", jsMap);
jsEngine.eval(
"for (var idx in map) print(idx + '; ' + map[idx] + '\\n');"
);
Otherwise, you're stuck with standard Java syntax.
Related
hi im trying to access a MultiValueMap which is in a Hashmap
this is my HashMap insideprojectDetails HashMap
private HashMap<String, ClassDetails> classDetailsMap = new HashMap<String, ClassDetails>();
inside that classDetailsMap i have MultiValueMap called methodDetailsMap
private MultiMap<String, MethodDetails> methodDetailsMap = new MultiValueMap<String, MethodDetails>();
when im trying to access the methodDetailsMap by
Set<String> methodNamesSet = projectDetails.getClassDetailsMap().get(cls).getMethodDetailsMap().keySet();
String[] methodNames = methodNamesSet.toArray(new String[0]);
for (int i = 0; i < methodNames.length; i++) {
String methodName = methodNames[i];
System.out.println(cls + " "+methodName);
//codes used to access key values
Collection coll = (Collection) methodNamesSet.get(methodName);
System.out.println(cls + " "+methodNamesSet.get(methodName));
}
i get a error get saying cannot resolve method get(java.lang.String)
is there any way to access the MultiValueMap
Its a compilation error with your code. There is no get method in Set.
methodNamesSet.get(methodName)
To get method details, first loop through the set and then get method details from methodDetailsMap as below.
MultiValueMap<String, MethodDetails> methodDetailsMap = projectDetails.getClassDetailsMap().get(0).getMethodDetailsMap();
Set<String> methodNamesSet = methodDetailsMap.keySet();
for(String str: methodNamesSet) {
System.out.println(methodDetailsMap.get(str));
}
I read your code and as I understand first you need to get all method names of cls class then you want to get them one by one. So in the for loop you need to get from the getMethodDetailsMap().This will help you:
for (int i = 0; i < methodNames.length; i++) {
String methodName = methodNames[i];
System.out.println(cls + " "+methodName);
//codes used to access key values
Collection coll = projectDetails.getClassDetailsMap().get(cls).getMethodDetailsMap().get(methodName);
System.out.println(cls + " "+methodNamesSet.get(methodName));
}
I've created a hash map that groups unique keys that combine three parameters, i.e. customer, sc and admin. I want to create a unique list of keys with a list of servers attached. I've implemented the following:
public static void main(String[] args) {
String items = "customer1^sc1^admin1|server1~" +
"customer1^sc1^admin1|server2~" +
"customer1^sc1^admin1|server3~" +
"customer2^sc1^admin1|server1~" +
"customer3^sc1^admin1|server3~" +
"customer3^sc1^admin1|server2~";
// Set up raw data
List<String> splitItems = Arrays.asList(items.split("\\s*~\\s*"));
// Display raw data
System.out.println("Raw List: " + items);
// Create a hash map containing customer name as key and list of logs as value
HashMap<String, List<String>> customerHashMap = new HashMap<>();
// Loop through raw data
for (String item : splitItems) {
// Create new lists. One for customers and one for logs
// List<String> customerList = new ArrayList<>();
List<String> logList;
String list[] = item.split("\\|");
String customer = list[0];
String log = list[1];
logList = customerHashMap.get(customer);
if (logList == null){
logList = new ArrayList<>();
customerHashMap.put(customer, logList);
}
logList.add(log);
// System.out.println(logList);
}
// Print out of the final hash map. Customer "a" should only have "a" logs, customer "b" with "b", etc.
System.out.println("");
List<String> hashMapList = new ArrayList<String>();
Iterator it = customerHashMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
String output = pair.getKey() + "|" + pair.getValue().toString();
hashMapList.add(output);
it.remove();
}
String hashMapResultString = hashMapList.toString();
String hashMapResultFormatted = hashMapResultString.replaceAll("[\\[\\]]", "");
System.out.println(hashMapResultFormatted);
}
Raw List: customer1^sc1^admin1|server1~customer1^sc1^admin1|server2~customer1^sc1^admin1|server3~customer2^sc1^admin1|server1~customer3^sc1^admin1|server3~customer3^sc1^admin1|server2~
Hash Map String:
customer2^sc1^admin1|server1, customer3^sc1^admin1|server3, server2, customer1^sc1^admin1|server1, server2, server3
I now want to use the hash map to create a string which will be parsed further (don't ask lol). So I set the keys and values of the hash map to a string which separates them with a unique delimiter |. The problem is that because the key is a List<String>, when printing I can't ascertain the beginning of every new key if its value is a list with more than one item, i.e. customer3^sc1^admin1|server3, server2, is followed immediately by customer1^sc1^admin1|server1, server2, server3. I need a delimiter here that separates them.
My ideal output would look like this:
customer2^sc1^admin1|server1~customer3^sc1^admin1|server3, server2~customer1^sc1^admin1|server1, server2, server3~...
How can I achieve this?
Update:
This is the answer I ultimately found useful for my particular problem:
StringBuilder s = new StringBuilder();
for (Map.Entry<String, List<String>> entry : customerHashMap.entrySet()) {
s.append(entry.getKey() + "|");
List<String> list = entry.getValue();
for (String item : list) {
if (item != list.get(list.size() - 1)) {
s.append(item + "^");
} else {
s.append(item);
}
}
s.append("~");
}
System.out.println(s.toString());
You can iterate through a map's entry set:
StringBuilder s = new StringBuilder();
for(Map.Entry<String,List<String>> entry : map.entrySet()) {
s.append(entry.getKey() + "\n");
List<String> list = entry.getValue();
for(String item : list) {
s.append(" " + item + "\n");
}
}
return s.toString();
For the sake of a clearer example, I've output a different format from the one you asked for, but this illustrates how to work with a map of list values. When adapting to your needs, have a look at java.util.StringJoiner and the related Collectors.joining(); it may well be useful.
Streams can be handy here:
String encoded = map.entrySet().stream()
.map( entry -> entry.getValue().stream()
.collect(Collectors.joining("^"))
+ "|" + entry.getKey())
.collect(Collectors.joining("~"));
What happens here is:
We get a stream of Entry<String,List<String> out of the map
The lambda entry -> ... converts each entry into a string of the form val1^v2^v3^...^valN|key, i.e. we are mapping a Stream<Entry<>> into a Stream<String>.
the final collect() joins the stream of strings into a single string using ~ as a delimiter.
i'm moving from php to Java. and i'm trying to achieve something like below in Java :
$user = array(
"firstname" => "myname",
"lastname" => "surname",
"phone" => array(
"home" => "123213213213",
"office" => "312321321312312",
"mobile" => "4532134213131312"
)
)
is there any way to do like that in java?
Thanks!
There are are few ways to make it closer, but nothing as convenient as that.
Example 1:
Map<String, Object> user = new HashMap<String, Object>() {
{
put("firstname", "myname");
put("lastname", "surname");
put("phone", new HashMap<String, String>() {
{
put("home", "123213213213");
put("office", "312321321312312");
put("mobile", "4532134213131312");
}
});
}
};
Update example:
((Map)user.get("phone")).put("mobile2", "123");
Adding another map:
user.put("address", new HashMap<String, Object>());
(could perhaps be improved by use of putIfAbsent method, or the merge-methods)
Printing current contents:
System.out.println(user);
Gives:
{firstname=myname, address={}, phone={mobile2=123, mobile=4532134213131312, office=312321321312312, home=123213213213}, lastname=surname}
For instance you can use the following code :
Map<String, Object> map = new HashMap<>();
map.put("firstname", "myname");
Map<String, String> phones = new HashMap<>();
phones.put("home" , "123213213213");
phones.put("office" , "312321321312312");
phones.put("mobile" , "4532134213131312");
map.put("phones", phones);
You could use the JSON API in Java to create an object in the format you like. Here is the documentation of the JSON API
http://www.oracle.com/technetwork/articles/java/json-1973242.html
Example:
String stringToParse = "{" +
"firstname: \"myname\"," +
"lastname: \"surname\"," +
"phone: [ " +
" { home: \"123213213213\" }, " +
" { office: \"312321321312312\" }," +
" { mobile: \"4532134213131312\" }" +
"]" +
"}";
JSONParser parser = new JSONParser();
JSONObject json = (JSONObject) parser.parse(stringToParse);
It is "possible" but I don't think it would be good convention (unless there is another way I am unaware of). I believe that this would work:
HashMap<String, Object> hm = new HashMap<String, Object>();
I would think that you would have to use a List object type instead of a array data type for a HashMap. If I were doing this, I would likely create my own class that handles this situation.
I am trying to map a meterId to a list of MeterBlinks that have that Id. I'm mainly confused on how to build the list up for the HashMap.put() call. Code below:
Map<String, List<MeterBlink>> IdToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
List<MeterBlink> meterBlinkList = new ArrayList<>();
meterBlinkList.add(meterBlink);
String meterId = meterBlink.getMeterId();
idToMetersMap.put(meterId, meterBlinkList)
}
I think the issue is that I am creating a new list each time I iterate through but I am not sure how to resolve this.
Use the computeIfAbsent method added in jre 8:
Map<String, List<MeterBlink>> idToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
String meterId = meterBlink.getMeterId();
idToMetersMap.computeIfAbsent(meterId, k -> new ArrayList<>()).add(meterBlinks);
}
Another option in java 8:
Map<String, List<MeterBlink>> idToMetersMap = createData().stream()
.collect(Collectors.groupingBy(MeterBlink::getMeterId));
I like the java8 answer, but here without java8 (without lambda expressions):
Map<String, List<MeterBlink>> idToMetersMap = new HashMap<>();
for (MeterBlink meterBlink : createData()) {
String meterId = meterBlink.getMeterId();
List<MeterBlink> meterBlinkList = idToMetersMap.get(meterId);
//if List doesn't exist create it and put in Map
if (meterBlinkList == null) {
meterBlinkList = new ArrayList<>();
idToMetersMap.put(meterId, meterBlinksList)
}
meterBlinkList.add(meterBlink);
}
Building an Android app that needs to parse a URL built with deep parameters using jQuery params().
Sample URL:
http://webapp.example.com/#params%5Bid%5D=33330&type=detail&channel=ss&view=Detail
The result should be JSON, which I can feed into Gson. Manual parsing is not optimal; a generic solution is preferable.
How can this task be done?
at first glance I thought you need a js/jquery solution. But on closer inspection, probably java. Anywhoo, I had already dont the former so you get both!
Js/jquery
url="http://webapp.example.com/#params%5Bid%5D=33330&type=detail&channel=ss&view=Detail";
delimatedString = url.substring(url.indexOf("#")+1,url.length); //remove everything before the #
var jsonObject = new Object();
$.each(delimatedString.split("&"), function(index, item) {//for loop to spit at the *&
//item is now in the syntax key=value
var keyValPair = item.split("="); //split each item at the = to seperate
var key = keyValPair[0];
var value = keyValPair[1];
jsonObject[key]=value;
});
java
String delimatedStrings = url.substring("&"+1, url.length)
String[] keyValuePairs = delimatedString.split("&");
Map<String, String> map = new HashMap<String, String>();
for (String keyValuePair : keyValuePairs)
{
String key = delimatedString.split("=")[0];
String value = delimatedString.split("=")[1];
map.put(key, value);
}
ps. I didnt compile the java, and you will need an external lib to take care of the map to JSON conversion.