I am trying to set variable value based on conditions and I want to know if there is a better way to do this in Java 8 rather than using multiple if else or switch statements.
I need to set Request Number based on String values
123 for foo,
123 for bar,
456 for xyz,
000 as default value i.e for any other string value.
if(someString.equalsIgnorecase("foo")){
x = someObj.setRequestNumber(123);
}
else if(someString.equalsIgnorecase("bar")){
x = someObj.setRequestNumber(123);
}
else if(someString.equalsIgnorecase("xyz")){
x = someObj.setRequestNumber(456);
}
else{
x = someObj.setRequestNumber(000);
}
Try this:
switch (someString.toUpperCase()) {
case "XYZ":
myNum = 1;
break;
case "FOO":
myNum = 2;
break;
case "BAR":
case "ANOTHER_BAR":
myNum = 3;
break;
default:
myNum = -1;
break;
}
From now on, with the new Switch features (JDK 14+), you can even simplify the Switch statement! Link: https://openjdk.java.net/jeps/361
First of all, you could replace the first two if statements with one:
if (someString.equalsIgnorecase("foo") || someString.equalsIgnorecase("bar")) {
x = someObj.setRequestNumber(123);
} else if (someString.equalsIgnorecase("xyz")) {
x = someObj.setRequestNumber(456);
} else {
x = someObj.setRequestNumber(000);
}
Another way is to create a Map, and lookup the result from the map:
Map<String, Integer> map = Map.of("foo", 123, "bar", 123, "xyz", 456);
x = map.getOrDefault(someString.toLowerCase(), 0);
(Note: Map.of(...) was added in Java 9, if you're using an earlier version of Java you'll have to add the elements separately with map.put("foo", 123); etc.).
Note: the code has been updated adding a toLowerCase when asking the value connected to a particular key. By the way, a key should be uniquely identified and the OP should decide if using lower cases or upper cases for the keys instead of changing the code making ignore case comparisons.
You can create a Map<String, Integer> and use it to set your value (this solution works also in older versions of java, eventually for versions before java 5 don't use generics):
// Init the map with the values for each key
Map<String, Integer> values = new HashMap<>();
values.put("foo", 123);
values.put("bar", 123);
values.put("xyz", 456);
// Use the values map to set your value
someObject.setRequestNumber(value.getOrDefault(someString.toLowerCase(), 0));
Use the getOrDefault method to set a default value if none of the key is present in the values map:
Returns the value to which the specified key is mapped, or defaultValue if this map contains no mapping for the key.
Note that if you need to assign the value 000 as default value this is not a n int. It must be set as a String to hold all the three zeros. Changing it to a String is simple just change the code as follow:
// Init the map with the values for each key
Map<String, String> values = new HashMap<>();
values.put("foo", "123");
values.put("bar", "123");
values.put("xyz", "456");
// Use the values map to set your value
// The method setRequestNumber must receive a String
someObject.setRequestNumber(value.getOrDefault(someString, "000"));
As alternative using the switch the best is to create a method like the following (since java 7):
public String getValueFromKey(String key) {
switch(key.toLowerCase()) {
case "foo":
case "bar": // You can combine multiple case
return "123"; // You can exit directly with a return instead
// of saving the result in a variable and use a break
case "xyz":
return "456";
default: // Use the default to set the handle any value not previously handled
return "000";
}
...
someObj.setRequestNumber(getValueFromKey(someString));
Use a map.
Initialization:
Map<String,Integer> map = new HashMap<>();
map.put("foo", 123);
map.put("bar", 123);
map.put("xyz", 456);
:
Use:
Integer req = map.get(someString.toLowerCase());
if (req != null)
someObj.setRequestNumber(req);
else
error("....!");
This trades off a little initialization code for simplicity elsewhere, and is worthwhile if you have to make 'many' such lookups and have 'many' strings to look up.
You can use a Map
Map<String,Integer> map = new HashMap<>();
map.put("foo",123);
map.put("bar",123);
map.put("xyz",456);
x= map.get(someString);
When faced with many conditions see if you can apply polymorphism .
https://refactoring.com/catalog/replaceConditionalWithPolymorphism.html
Related
I am currently working on an assignment for school where I am supposed to create a hashmap within a hashmap like this:
Map<String, Map<String, Integer>> girlsByYear = new HashMap<>();
Map<String, Map<String, Integer>> boysByYear = new HashMap<>();
According to the professor, there are not supposed to be any other maps needed to complete the assignment. The maps add elements to them by accessing files within a package that contain names, gender, and rank of baby names by year. When the program is compiled and finished, a JavaFX chart is created that requests a girl or boy's name to be entered. When entered, the chart shows by year the rank of how popular the name was.
Currently, I have figured most of it out, but I cannot understand how to access the Hashmap within the first Hashmap without a key for the first hashmap. By that, I mean that I am supposed to have a check performed by the textbox for the JavaFX class that reviews whether the name is in the Hashmap or not. Here is my code:
public class NameHelper {
// Declare the hash maps.
Map<String, Map<String, Integer>> girlsByYear = new HashMap<>();
Map<String, Map<String, Integer>> boysByYear = new HashMap<>();
// Declare addition variables.
String firstWord = "";
String secondWord = "";
String thirdWord = "";
Integer rank;
String fileName;
// This method will load the files from the data package, review the files,
// and add each item respectively to either map.
public void load() throws FileNotFoundException {
File dir = new File("src/data");
File [] files = dir.listFiles();
// for each file in the directory...
for (File f : files)
{
// Get the file name and split the year from it to add to each name.
String newFileName = f.getName();
fileName = newFileName.replaceAll("[yobtxt.]","");
Scanner scanner = new Scanner(f);
// While the files are not empty.
while(scanner.hasNextLine()) {
// If the second column split by a delimiter is M then add the information
// to the boys. Else girls.
String input = scanner.nextLine();
// Set the input to string values to enter into each hash map.
String initial = input.split(",")[1];
firstWord = fileName;
secondWord = (input.split(",")[0]).toLowerCase();
thirdWord = input.split(",")[2];
rank = Integer.parseInt(thirdWord);
// Use a switch statements since if statements aren't working.
switch(initial) {
case "M":
boysByYear.put(firstWord, new HashMap<String, Integer>());
boysByYear.get(firstWord).put(secondWord, rank);
break;
case "F":
girlsByYear.put(firstWord, new HashMap<String, Integer>());
girlsByYear.get(firstWord).put(secondWord, rank);
break;
default:
System.out.println("This is an issue");
break;
}
}
// Close the scanner.
scanner.close();
}
}
// This method will return a sorted set of years by getting the keyset from the hashmaps.
public Set<String> getYears() {
// Create the set.
Set<String> set = new HashSet<>();
// Add all the years of the listed by file name.
for(String key : girlsByYear.keySet()) {
set.add(key);
}
// Convert the set to a sorted set.
TreeSet<String> treeSet = new TreeSet<>(set);
return treeSet;
}
// This method will return true if the supplied name is found in the data structure.
// Use the gender input to determine which map to search by using "containsKey".
public boolean isNamePresent(String name, String gender) {
if(gender == "M") {
//Check if the name is within the map's map.
if(boysByYear.get(name).containsKey(name)) {
return true;
}
}
else if(gender == "F") {
if(girlsByYear.containsKey(name.toLowerCase())) {
return true;
}
}
return false;
}
The section that I need help with is the isNamePresent method. I need to check if the name is in the key of the second hashmap which is set up in this format (String year, HashMap(String name, Integer rank))
Any help or guidance would be greatly appreciated!
Additional notes: The JavaFx section for the chart was provided by the professor.
One thing you need to fix first is comparing the strings using ==. This doesn't work unless both the string passed as gender parameter is a string literal. You need to use equals instead, see How do I compare strings in Java? (switch does this automatically).
Furthermore you should avoid duplicating code by retrieving the map to a local variable:
Map<String, Map<String, Integer>> map;
switch (gender) {
case "M":
map = boysByYear;
break;
case "F":
map = girlsByYear;
break;
default:
return false; // alternatively throw new IllegalArgumentException();
}
To find out, if at least one of the maps contains name as a key, go through all the values and check the maps:
final String nameLower = name.toLowerCase();
return map.values().stream().anyMatch(m -> m.containsKey(nameLower));
BTW: You need to fix the way you read the data. Otherwise you'll get at most one name per year&gender, since you replace the Map. Furthermore I recommend storing the result of split instead of invoking it 3 times. Also don't use fields as variables only needed in a loop and choose more discriptive variable names:
Map<String, Integer> boys = new HashMap<>();
Map<String, Integer> girls = new HashMap<>();
boysByYear.put(fileName, boys);
girlsByYear.put(fileName, girls);
while(scanner.hasNextLine()) {
// If the second column split by a delimiter is M then add the information
// to the boys. Else girls.
String input = scanner.nextLine();
String[] parts = input.split(",");
// Set the input to string values to enter into each hash map.
String gender = parts[1];
String name = parts[0].toLowerCase();
int rank = Integer.parseInt(parts[2]);
switch(gender) {
case "M":
boys.put(name, rank);
break;
case "F":
girls.put(name, rank);
break;
default:
System.out.println("This is an issue");
break;
}
}
To access the inner hash map without knowing the key of the outer map, you can iterate over each entry of the outer map.
for(Map.Entry<String, Integer> mapEntry: boysByYear.entrySet()){
// Get the innerMap and check if the name exists
Map<String, Integer> innerMap = mapEntry.getValue();
if(innerMap.containsKey(name)){
return true;
}
}
Consider the following scenario, where rules defined by ID need to satisfy all conditions .
RULES
ID Attribute1 Operator1 Value1 Attribute2 Operator2 Value1 ... And so on
-------------------------------------------------------------------
1 x = 10 a IN 5,10
2 x = 20 a IN 10,15
3 x = 20 a IN 25,20
The above means if(x=10) & if(a = 5 or a = 10} & ..., then ID = 1
In case of an incoming feed for the format [x, a, ...]
10, 5, ...
10, 10, ...
20, 20, ...
then IDs should be
1
1
3
I wish to check if there is a more easy and elegant way than the below solution and which data structure to be used.
What are the cons of the data structures & approach used in the below solution? (sure there could be)
Assumptions:
The list of attributes is fixed for now but may change.
The incoming feed format is fixed.
Not on the lines of Java rules implementation.
Algorithmic answer is accepted, Java 7 implementation is preferred.
My solution:
The rule set is organized as Map<String, List<Criterion>>, where key is ID and Criterion contains attribute, operator and value as fields.
The data is organized as List<Map<String, String>>, where key is the attribute (could be x or a etc.) and value as the actual value in the feed.
For each entry in List<Map<String, String>, do below to find the ID
Loop through each of the entries in Map<String, List<Criterion>> and invoke the below method. In case true is returned, I log the ID, which is the key and break the rule loop & proceeed to next data item.
public boolean executeRules(List<Criterion> list,
Map<String, String> dataItem) {
boolean fullMatch = true;
if(CollectionUtils.isNotEmpty(list)){
for(Criterion rule : list) {
switch (rule.getOperator()) {
case EQUAL_TO: //Similar to EQUAL TO
if(!rule.getValue().equals(dataItem.get(rule.getOperand()))){
fullMatch = false;
}
break;
case IN://Similar to IN
List<String> inCriteria = null;
if(rule.getValue() != null) {
inCriteria = Arrays.asList(((String) rule.getValue()).split("\\s*,\\s*"));
if(!inCriteria.contains(dataItem.get(rule.getOperand()))){
fullMatch = false;
}
} else {
fullMatch = false;
}
break;
default:
LOG.error("Invalid Operator: " + rule.getOperator());
fullMatch = false;
break;
}
if(!fullMatch) {
return false;
}
}
} else {
LOG.error("No Rules found");
return false;
}
return true;
}
PS: Not a homework; just some research.
I think that you could make use of SmartParam library (http://smartparam.org/). It is designed for such evaluations and is very generic, so you can use if even with properties file as well as with database
Below is data from 2 linkedHashMaps:
valueMap: { y=9.0, c=2.0, m=3.0, x=2.0}
formulaMap: { y=null, ==null, m=null, *=null, x=null, +=null, c=null, -=null, (=null, )=null, /=null}
What I want to do is input the the values from the first map into the corresponding positions in the second map. Both maps take String,Double as parameters.
Here is my attempt so far:
for(Map.Entry<String,Double> entryNumber: valueMap.entrySet()){
double doubleOfValueMap = entryNumber.getValue();
for(String StringFromValueMap: strArray){
for(Map.Entry<String,Double> entryFormula: formulaMap.entrySet()){
String StringFromFormulaMap = entryFormula.toString();
if(StringFromFormulaMap.contains(StringFromValueMap)){
entryFormula.setValue(doubleOfValueMap);
}
}
}
}
The problem with doing this is that it will set all of the values i.e. y,m,x,c to the value of the last double. Iterating through the values won't work either as the values are normally in a different order those in the formulaMap. Ideally what I need is to say is if the string in formulaMap is the same as the string in valueMap, set the value in formulaMap to the same value as in valueMap.
Let me know if you have any ideas as to what I can do?
This is quite simple:
formulaMap.putAll(valueMap);
If your value map contains key which are not contained in formulaMap, and you don't want to alter the original, do:
final Map<String, Double> map = new LinkedHashMap<String, Double>(valueMap);
map.keySet().retainAll(formulaMap.keySet());
formulaMap.putAll(map);
Edit due to comment It appears the problem was not at all what I thought, so here goes:
// The result map
for (final String key: formulaMap.keySet()) {
map.put(formulaMap.get(key), valueMap.get(key));
// Either return the new map, or do:
valueMap.clear();
valueMap.putAll(map);
for(Map.Entry<String,Double> valueFormula: valueMap.entrySet()){
formulaMap.put(valueFormula.getKey(), valueFormula.value());
}
I am new to Java. I want to Parse the data which is in this Format
Apple;Mango;Orange:1234;Orange:1244;...;
There could be more than one "Orange" at any point of time. Numbers (1,2...) increase and accordingly as the "Orange".
Okay. After splitting it, Lets assume I have stored the first two data(Apple, Orange) in a variable(in setter) to return the same in the getter function. And now I want to add the value(1234,1244....etc) in the 'orange' thing into a variable to return it later. Before that i have to check how many oranges have come. For that, i know i have to use for loop. But don't know how to store the "Value" into a variable.
Please Help me guys.
String input = "Apple;Mango;Orange:1234;Orange:1244;...;"
String values[] = input.split(";");
String value1 = values[0];
String value2 = values[1];
Hashmap< String, ArrayList<String> > map = new HashMap<String, ArrayList<String>>();
for(int i = 2; i < values.length; i = i + 2){
String key = values[i];
String id = values[i+1];
if (map.get(key) == null){
map.put(key, new ArrayList<String>());
}
map.get(key).add(id);
}
//for any key s:
// get the values of s
map.get(s); // returns a list of all values added
// get the count of s
map.get(s).size(); // return the total number of values.
Let me try to rephrase the question by how I interpreted it and -- more importantly -- how it focuses on the input and output (expectations), not the actual implementation:
I need to parse the string
"Apple;Mango;Orange:1234;Orange:1244;...;"
in a way so I can retrieve the values associated (numbers after ':') with the fruits:
I should receive an empty list for both the Apple and Mango in the example, because they have no value;
I should receive a list of 1234, 1244 for Orange.
Of course your intuition of HashMap is right on the spot, but someone may always present a better solution if you don't get too involved with the specifics.
There are a few white spots left:
Should the fruits without values have a default value given?
Should the fruits without values be in the map at all?
How input errors should be handled?
How duplicate values should be handled?
Given this context, we can start writing code:
import java.util.*;
public class FruitMarker {
public static void main(String[] args) {
String input = "Apple;Mango;Orange:1234;Orange:1244";
// replace with parameter processing from 'args'
// avoid direct implementations in variable definitions
// also observe the naming referring to the function of the variable
Map<String, Collection<Integer>> fruitIds = new HashMap<String, Collection<Integer>>();
// iterate through items by splitting
for (String item : input.split(";")) {
String[] fruitAndId = item.split(":"); // this will return the same item in an array, if separator is not found
String fruitName = fruitAndId[0];
boolean hasValue = fruitAndId.length > 1;
Collection<Integer> values = fruitIds.get(fruitName);
// if we are accessing the key for the first time, we have to set its value
if (values == null) {
values = new ArrayList<Integer>(); // here I can use concrete implementation
fruitIds.put(fruitName, values); // be sure to put it back in the map
}
if (hasValue) {
int fruitValue = Integer.parseInt(fruitAndId[1]);
values.add(fruitValue);
}
}
// display the entries in table iteratively
for (Map.Entry<String, Collection<Integer>> entry : fruitIds.entrySet()) {
System.out.println(entry.getKey() + " => " + entry.getValue());
}
}
}
If you execute this code, you will get the following output:
Mango => []
Apple => []
Orange => [1234, 1244]
This question already has answers here:
Does JavaScript have an implementation of a set data structure?
(6 answers)
Closed 8 years ago.
I want to use a data structure in JavaScript that can be used to store number of IDs. I should be able to check if a key already exists in that set, something like Java Sets.
I want to achive same behaviours as follows (this code is in Java):
Set<String> st = new HashSet<String>();
//add elemets
if(st.contains("aks") ){
//do something
}
I want a JavaScript/dojo equivalent of the above code.
I've written a JavaScript HashSet implementation that does what you want and allows any object to be a member of the set: http://code.google.com/p/jshashtable
However, if you just need to store strings, you could do something more simply by storing set members as property names of a normal Object. For example:
function StringSet() {
var setObj = {}, val = {};
this.add = function(str) {
setObj[str] = val;
};
this.contains = function(str) {
return setObj[str] === val;
};
this.remove = function(str) {
delete setObj[str];
};
this.values = function() {
var values = [];
for (var i in setObj) {
if (setObj[i] === val) {
values.push(i);
}
}
return values;
};
}
A note about the implementation: val is an object used internally by the StringSet implementation that is unique to each set. Comparing property values of the object whose property names make up the set (setObj) against val eliminates the need for a hasOwnProperty() check and guarantees that only strings that have been added to the set will show up in values.
Example usage:
var set = new StringSet();
set.add("foo");
set.add("bar");
alert(set.contains("foo")); // true
alert(set.contains("baz")); // false
set.values(); // ["foo", "bar"], though not necessarily in that order
set.remove("foo");
set.values(); // ["bar"]
Why not use a normal object and check if a key exists with JavaScript's hasOwnProperty?
var x = {};
x['key'] = 'val';
x.hasOwnProperty('key'); // true //
x.hasOwnProperty('key2'); // false //
And here is a more advanced use case:
var x = {};
var prefix = 'item_';
for(var i=0;i<10;i++){
x[prefix+i] = 'value '+(i+1);
}
x.hasOwnProperty('item_6'); // true //
x.hasOwnProperty('other key'); // false //
Removing items can be done like this:
delete x['key'];
No Dojo needed, this is native to Javascript. Use Objects. Sounds like you only need the keys, not the values. Lookup is constant time.
var st = {'aks':1, 'foo':1, 'bar':1}; // or could start with empty {}. 1 could be any value of any type, it's just short.
//add elements
st.baz = 1;
//or load up dynamically
myArrayOfStrings.forEach(function(key){
st[key] = 1;
});
if("aks" in st){
//do something
}
Possibly with an associative array / Hashtable / dictionary (I don't know how it's called exactly), using the set elements as keys and "anything else" as values.
insert: mySet[key] = "Whatever";
delete: mySet[key] = null;
check: if (mySet[key] != null) { ... }
Hash is good candidate for implementing Set. You could create a set using a function like that:
function set () {
var result = {};
for (var i = 0; i < arguments.length; i++) result[arguments[i]] = true;
return result;
}
For instance:
x = set([1,2,2,4])
x[1] #==> true
x[3] #==> false
x[5] = true; # add element to the set
x[5] = false; # remove element from the set
Sets don't have keys. They only have set of values, but maps have pairs of key/value entities.
As a result, you have 2 options. Each of them has its drawbacks and advantages:
You can use as described above JavaScript object. Actually it is a map/associative array/hash table. One of its advantage - you can guarantee with this kind of structure that keys - are unique items. Its drawback connected to the issue - you have to keep some extra information that you don't need at all. Values of maps. trues or some other values. It does not matter. Why do you need them?
To resolve the previous disadvantage you may consider using JavaScript arrays. But, you'll have to write some wrappers so arrays's behavior will look like sets behavior. Also operations that will search by the uniqueId will be slower than the same ones for hashtables cause you'll have to iterate via all items of an array.
So, I think you should prefer hashtables to arrays, examples you can find in other posts. But probably you should consider changing of your data structure. don't keep uniqueId as keys with unselerss values if its possible. Let your unique ids point to some real objects for which these unique ids are used.
PS: one more thing. Arrays are also objects actually. As a result they can be used as hashtables/maps too.