I have narrowed down my problem to the following, I need to convert a String to a Item Identifyer.
Example:
String str = "pickaxe";
Item pick = str;
That would resolve to Identifying pick as pickaxe. How can you do this?
I think what you want to use is a Hash Map
In your code you can create your objects
Item pickaxe = new Pickaxe();
Hashmap<String, Item> items = new HashMap<String, Item>();
items.add("pickaxe", pickaxe);
Then later you can retrieve your item with
items.get("pickaxe");
Usually I would recommend an enum for this sort of string-to-Object lookup, but I also know that there's a huge list of items in minecraft that will change from game update to the next. That means that putting those around 370 items into an enum is a lot of work.
My suggestion: store them in a database or something else that is not your sourcecode. Then load it into a structure such as a HashMap or a List, the first one being easier for lookup up the short name, just as joey.enfield suggested. With a list, you would have to iterate through the whole thing to find an item with a matching name.
EDIT:
public enum Item {
PICKAXE("pickaxe"),
SHOVEL("shovel"),
BOW("bow");
private String m_shortID;
Item(String shortID){
m_shortID = shortID;
)
static Item lookupByShortID(String shortID){
for(Item i:values()){
if(i.m_shortID.equals(shortID)){
return i;
}
}
return null;
}
}
Here is a quick and dirty class I cooked up. You should be aware that this code will most likely produce a hashmap that does not contain EVERY item as there are most likely items that have the same String value for getItemDisplayName(), so the last one in Item.itemsList[] will be the one in the hashmap. Also note that it will produce a different list if you use a different MC language since getItemDisplayName() gets the localized String.
import java.util.HashMap;
import java.util.Map;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
public class ItemListByString
{
private Map<String, Item> itemMap;
private static ItemListByString instance = new ItemListByString();
private ItemListByString()
{
itemMap = new HashMap<String, Item>();
for (Item item : Item.itemsList)
if (item != null)
itemMap.put(item.getItemDisplayName(new ItemStack(item, 1, 0)), item);
}
public Item getItemByString(String s)
{
return instance.itemMap.get(s);
}
}
Related
I receive the following error when attempting to run my java program
Exception in thread "main" java.lang.ClassCastException: class java.util.TreeMap cannot be cast to class java.lang.Comparable (java.util.TreeMap and java.lang.Comparable are in module java.base of loader 'bootstrap')
at java.base/java.util.TreeMap.compare(TreeMap.java:1569)
at java.base/java.util.TreeMap.addEntryToEmptyMap(TreeMap.java:776)
at java.base/java.util.TreeMap.put(TreeMap.java:785)
at java.base/java.util.TreeMap.put(TreeMap.java:534)
at exportsParser.exportsMap(exportsParser.java:53)
at exportsParser.main(exportsParser.java:28)
The applicable code:
import edu.duke.*;
import java.io.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.*;
import java.util.*;
import java.util.TreeMap;
import java.util.Iterator;
public class exportsParser{
void println(Object obj){
System.out.println(obj);
}
/* The rather involved pattern used to match CSV's consists of three
* alternations: the first matches aquoted field, the second unquoted,
* the third a null field.
*/
private final static Pattern csv_pattern = Pattern.compile("\"([^\"]+?)\",?|([^,]+),?|,");
public static void main(String[] argv) throws IOException {
//println(csv_pattern);
exportsParser parser = new exportsParser();
BufferedReader reader = new BufferedReader(new FileReader("./exports_small.csv"));
parser.exportsMap(reader);
}
public TreeMap<String, TreeMap<TreeMap<String,String> ,TreeMap<String, String>>> exportsMap(BufferedReader reader) throws IOException{
if(reader.readLine() == null) return null;
TreeMap<String, TreeMap<TreeMap<String,String>, TreeMap<String,String>>> exportsTable = new TreeMap<>();
TreeMap<String, String> products = new TreeMap<>();
TreeMap<String, String> value = new TreeMap<>();
TreeMap<TreeMap<String,String>,TreeMap<String,String>> exportsData = new TreeMap<>();
int countryIndex = 0;
ArrayList<String> exportsList = new ArrayList<String>();
String line;
try{
while((line = reader.readLine()) != null){
exportsList = parse(line);
String countryName = exportsList.get(0);
products.put("items", exportsList.get(1));
value.put("total", exportsList.get(2));
println(products);
println(value);
exportsData.put(products, value);
println(exportsData);
// exportsTable.put(countryName,exportsData);
println(exportsTable);
}
}
catch(IOException e)
{
e.printStackTrace();
}
reader.close();
return exportsTable;
}
/* Parse one line.
* #return List of Strings, minus their double quotes
*/
public ArrayList<String> parse(String line) {
ArrayList<String> list = new ArrayList<String>();
Matcher mat = csv_pattern.matcher(line);
// For each field
while (mat.find()) {
String match = mat.group();
if (match == null)
break;
if (match.endsWith(",")) { // trim trailing ,
match = match.substring(0, match.length() - 1);
}
/*if (match.startsWith("\"")) { // assume also ends with
match = match.substring(1, match.length() - 1);
}*/
if (match.length() == 0)
match = null;
list.add(match);
}
return list;
}
}
To clarify, the issue arises when attempting to put the TreeMap data of products and value in exportsData. Same is applicable when attempting to add exportsData to the exportsTable correlating its key (Country) to the exportsData (Value). I understand what the errors means, I just have no idea as to how to fix it. Additionally libraries are not allowed (Purpose is to understand the flow of input data into "rows/columns" and experiment with Trees, HashMaps, etc)
Additionally, I cannot use a database for this as this is a requirement to manually do this. However what is not a requirement is using TreeMaps of course. We are allowed to experiment with the various Collection classes.
I have spent a while trying to get this to work but I have run out of thoughts and forum pages to read now. Eventually, this would be ideal to make it cater towards larger CSV files of unknown columns. However, for the practice run, we have been given the information before hand, hence the indexing in the code above.
CSV data:
Country,Exports,Value (dollars)
Germany,"motor vehicles, machinery, chemicals","$1,547,000,000,000"
Macedonia,"tobacco, textiles","$3,421,000,000"
Madagascar,"coffee, vanilla, shellfish","$864,800,000"
Malawi,"tea, sugar, cotton, coffee","$1,332,000,000"
Malaysia,"semiconductors, wood","$231,300,000,000"
Namibia,"diamonds, copper, gold, zinc, lead","$4,597,000,000"
Peru,"copper, gold, lead, zinc, tin, coffee","$36,430,000,000"
Rwanda,"coffee, tea, hides, tin ore","$720,000,000"
South Africa,"gold, diamonds, platinum","$97,900,000,000"
United States,"corn, computers, automobiles, medicines","$1,610,000,000,000"
This is my first time using the above so it is prone to beginner errors.
Check javadoc. To cite - The map is sorted according to the natural ordering of its keys, or by a Comparator provided at map creation time, depending on which constructor is used. Natural ordering of keys means key must implement Comparable. Your keys are of type TreeMap, which does not have natural ordering - it does not implement Comparable. Naturally this leads to ClassCastException.
If you really must use TreeMap as key for your TreeMap, you must provide a Comparator to TreeMap constructor:
Comparator<TreeMap<String,String>> comparator = implement it;
TreeMap<TreeMap<String,String>,TreeMap<String,String>> exportsData = new TreeMap<>(comparator);
Seeing that your data is coming from a csv file, i would suggest to parse it to some custom class. It would be much more readable, and it will be easier to implement Comparable, or Comparator if needed.
Edit: You don't actually need 2 maps - for exports and value, it complicates thing more that needed. Those can be put in a single map. Keys are values from the first line in csv(or other keys, as in your case) and values are corresponding values from the parsed line. So you have:
Map<String, String> lineData;
Country may also be part of this map(if you need it). Normally it's this map, which will be rerpesented by your custom class, but it looks like your task is to work with collections, so i won't delve into that.
Since you want to map country names to data, now you need another map - keys will be string(country name) and values the map containing line data from above.
All of that can be stored in a list(you can store anything in a list). I'm leaving to you figuring out the exact way to implement it.
I am currently working on one of the usecases where you are given 6 strings which has 3 oldValues and 3 newValues like given below:
String oldFirstName = "Yogend"
String oldLastName = "Jos"
String oldUserName = "YNJos"
String newFirstName = "Yogendra"
String newLastName ="Joshi"
String newUserName = "YNJoshi"
now what I basically want to do is compare each of the oldValue with its corresponding new value and return true if they are not equal i.e
if(!oldFirstName.equalsIgnoreCase(newFirstName)) {
return true;
}
Now, since I am having 3 fields and it could very well happen that in future we might have more Strings with old and new value I am looking for an optimum solution which could work in all cases no matter how many old and new values are added and without having gazillions of if else clauses.
One possibility I thought was of having Old values as OldArrayList and new values as newArraylist and then use removeAll where it would remove the duplicate values but that is not working in some cases.
Can anyone on stack help me out with some pointers on how to optimum way get this done.
Thanks,
Yogendra N Joshi
you can use lambdaj (download here,website) and hamcrest (download here,website), this libraries are very powerfull for managing collections, the following code is very simple and works perfectly:
import static ch.lambdaj.Lambda.filter;
import static ch.lambdaj.Lambda.having;
import static ch.lambdaj.Lambda.on;
import static org.hamcrest.Matchers.isIn;
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<String> oldNames = Arrays.asList("nameA","nameE","nameC","namec","NameC");
List<String> newNames = Arrays.asList("nameB","nameD","nameC","nameE");
List<String> newList = filter(having(on(String.class), isIn(oldNames)),newNames);
System.out.print(newList);
//print nameC, nameE
}
}
With this libraries you can solve your problem in one line. You must add to your project: hamcrest-all-1.3.jar and lambdaj-2.4.jar Hope this help serve.
NOTE: This will help you assuming you can have alternatives to your code.
You can use two HashMap<yourFieldName, yourFieldValue> instead of two Arrays / Lists / Sets of Strings (or multiple random Strings);
Then you need a method to compare each value of both maps by their keys;
The result will be an HashMap<String,Boolean> containing the name of each field key, and true if the value is equal in both maps, while false if it is different.
No matter how many fields you will add in the future, the method won't change, while the result will.
Running Example: https://ideone.com/dIaYsK
Code
private static Map<String,Boolean> scanForDifferences(Map<String,Object> mapOne,
Map<String,Object> mapTwo){
Map<String,Boolean> retMap = new HashMap<String,Boolean>();
Iterator<Map.Entry<String, Object>> it = mapOne.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Object> entry = (Map.Entry<String,Object>)it.next();
if (mapTwo.get(entry.getKey()).equals(entry.getValue()))
retMap.put(entry.getKey(), new Boolean(Boolean.TRUE));
else
retMap.put(entry.getKey(), new Boolean(Boolean.FALSE));
it.remove(); // prevent ConcurrentModificationException
}
return retMap;
}
Test Case Input
Map<String,Object> oldMap = new HashMap<String,Object>();
Map<String,Object> newMap = new HashMap<String,Object>();
oldMap.put("initials","Y. J.");
oldMap.put("firstName","Yogend");
oldMap.put("lastName","Jos");
oldMap.put("userName","YNJos");
oldMap.put("age","33");
newMap.put("initials","Y. J.");
newMap.put("firstName","Yogendra");
newMap.put("lastName","Joshi");
newMap.put("userName","YNJoshi");
newMap.put("age","33");
Test Case Run
Map<String,Boolean> diffMap = Main.scanForDifferences(oldMap, newMap);
Iterator<Map.Entry<String, Boolean>> it = diffMap.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String,Boolean> entry = (Map.Entry<String,Boolean>)it.next();
System.out.println("Field [" + entry.getKey() +"] is " +
(entry.getValue()?"NOT ":"") + "different" );
}
You should check too if a value is present in one map and not in another one.
You could return an ENUM instead of a Boolean with something like EQUAL, DIFFERENT, NOT PRESENT ...
You should convert your String to some Set.
One set for OLD and another for NEW. And your goal of varity number of elements will also be resolved using same.
As it's set order of it will be same.
I have a problem related to "dynamic ArrayLists". I have a List that contains usernames and their data. I want for every distinct username to create a single list that contains all data of this user. For example, I have an arraylist (username,tweet) that has: lefteris,"Plays ball", Kostas, "Plays basketball", lefteris, "Nice weather". And I want after that to create two lists. One list with kostas and his tweets and another with lefteris and its tweets (2 tweets). The parent arraylist may have 20 distinct usernames or more. How can I do that ?
I recommend you to use hashmap or hashset instead because if you need to store something in pairs, hashing is a perfect solution......
I'd go with the following data structure:
HashMap<String, ArrayList<String>>
Then you could manipulate a "dynamic" list of properties keyed to each name, if the properties are single items:
Lefteris->("Plays ball", "Nice weather",...)
Kostas->("Plays basketball",...)
If the properties are key-value pairs, do:
HashMap<String, HashMap<String, Object>>
Data looking like:
Lefteris->(Sport->"Plays ball", Weather->"Nice",...)
Kostas->(Sport->"basketball",...)
Since you parse the items from a file, you can do the following.
Create a map that contains the tweets associated to a particular username
Map<String,List<String>> userTweets = new HashMap<String,List<String>>();
Then, have a method to associate a tweet to certain user, verifying that it is already added in the map and adding it if it isn't.
public void addTweetToUser(String user, String tweet) {
if(userTweets.containsKey(user))
userTweets.get(user).add(tweet);
else {
List<String> newUserTweets = new LinkedList<String>();
newUserTweets.add(tweet);
userTweets.put(user, newUserTweets);
}
}
As a plus, you can improve this by creating an object UserTweet that contains:
public class UserTweet {
private String user;
private String tweet;
//Constructor, Setters & Getters or all of them
}
Then your addTweetToUser method can have an UserTweet parameter instead.
When you want to know the tweets for a certain user, you just obtain the corresponding list from the userTweets map. I alsomethods to remove tweets and/or remove users, just in case.
Several libraries add excellent collection-processing functionality to Java along the lines of what functional languages provide. One such library is Google Guava. Guava provides a MultiMap suitable for grouping things the way you want. There are also many utility methods, like MultiMaps.index(), which collects items from a list into a map by applying some function to the elements of the list to calculate a key. With such support, it only takes a few lines of code and one Function implementation (a closure in any other language) to solve your problem:
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Multimaps;
import java.util.Arrays;
import java.util.List;
public class Tweets {
public static final int NAME = 0;
public static final int TWEET = 1;
public static void main(String[] args) {
List<String> namesAndTweets = Arrays.asList(
"lefteris", "Plays ball",
"Kostas", "Plays basketball",
"lefteris", "Nice weather");
List<List<String>> nameTweetPairs =
Lists.partition(namesAndTweets, 2);
Multimap<String, List<String>> namesAndTweetsByName =
Multimaps.index(nameTweetPairs, get(NAME));
Multimap<String, String> tweetsByName =
Multimaps.transformValues(namesAndTweetsByName, get(TWEET));
System.out.println(tweetsByName);
}
private static Function<List<String>, String> get(final int n) {
return new Function<List<String>, String>() {
#Override
public String apply(List<String> nameAndTweet) {
return nameAndTweet.get(n);
}
};
}
}
Outputs:
{lefteris=[Plays ball, Nice weather], Kostas=[Plays basketball]}
Update: To explain the code a bit more, there are three basic steps:
Take the list that has names and tweets all mixed together and use Lists.partition() to break it into pairs of (name, tweet).
Use MultiMaps.index() to build a MultiMap from the pairs, taking the name as the map key. This gives you a map where map keys are names and map values are the (name, tweet) pairs.
Use MultiMaps.transformValues() to reduce the map values from (name, tweet) pairs to just the tweets.
P.S. does anyone know if there's a built-in Function that does what my get() does? It seems like a useful Function that should be provided, but I can't find it anywhere.
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;
I have a List of Strings and I'm trying to have a method that tells me which String has more occurrences in the List.
Here is what I've done so far:
package codekata;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class OcurrenciasEnLista {
public static void main(String[] args) {
List<String> lista = new ArrayList<String>();
lista.add("test");
lista.add("foo");
lista.add("foo");
lista.add("foo");
lista.add("bar");
lista.add("crack");
moreOftenWord(lista);
}
private static void moreOftenWord(List<String> lista) {
Map<String, Integer> mapa = new HashMap<String, Integer>();
for (String palabra: lista)
addOrIncrementCount(mapa, palabra);
}
private static void addOrIncrementCount(Map<String, Integer> counters,
String toAdd) {
Integer currValue = counters.get(toAdd);
if (currValue == null)
counters.put(toAdd, 1);
else
{
counters.put(toAdd, currValue + 1);
}
}
}
What I don't know how to do is to return -the word- already in the moreOftenWord method.
Can anybody give me a clue on this?
The most common element in a list is called the "mode" of the list.
http://www.dreamincode.net/forums/topic/39745-get-mode-of-a-list/ is the first result for "mode of a list java" that looks relevant in case you want a code sample.
To get at the most common element after you've built your map of counters, you probably want to do something like
Map.Entry<String, Integer> mode = null;
for (Map.Entry<String, Integer> e : counters.entrySet()) {
if (mode == null || mode.value() < e.value()) {
mode = e;
}
}
// Most common string in mode.getKey()
This assumes that you redefine
Map counters
generically as
Map<String, Integer> counters
The Multiset data structure maintains a count of each element added to it. So you can remove all boilerplate code if you use this. Then all you need to do is iterate through the Multiset and find the element that has the max count.
Guava library has many such useful data structures and more.
In statistics, this is called the "mode" (as Mike's answer already explained). jOOλ is a library that supports mode() on streams. The following program:
System.out.println(
Seq.of("test", "foo", "foo", "foo", "bar", "crack")
.mode()
);
Yields:
Optional[foo]
(disclaimer: I work for the company behind jOOλ)