Reputation: 3
Something fundamental about hashmaps and for loops is hard for me to grasp here. What I'm trying to do is add +1 to the value associated with the key based on the Keys method every time the value within the array list is associated with the key string.
So if there are 3 values in the array list that are positive then the hashmap should have the value with the key "positive" updated to 3.
Any help/advice would be appreciated - thank you.
public String Keys(double input){
if (input > 0){
System.out.println("positive");
}
else if (input < 0) {
System.out.println("negative");
}
else if (input == 0) {
System.out.println("zero");
}
return "";
}
public HashMap<String, Integer> increaseValues(ArrayList<Double> inputs){
HashMap<String, Integer> hashMap = new HashMap<>();
hashMap.put("positive", 0);
hashMap.put("negative", 0);
hashMap.put("zero", 0);
//What I tried before adding the Keys method.
//This updates the value but the loop won't continue if another input in the
//arraylist is true.
for (int i = 0; i < inputs.size(); i++){
double input = inputs.get(i);
if (input > 0){
hashMap.put("positive", 1);
} else if (input < 0){
hashMap.put("negative", 1);
} else if (input == 0){
hashMap.put("zero", 1); }
return hashMap;
}
public void main(String[] args){
ArrayList<Double> inputs = new ArrayList<>();
inputs.add(-4.56);
inputs.add(-4.66);
inputs.add(0.0);
inputs.add(6.0);
inputs.add(-6.99);
inputs.add(6.97);
}
Upvotes: 0
Views: 2493
Reputation: 5336
The method put
of HashMap maps unique keys with values. It replaces the existing value with the new value in case the provided key is already present in the map.
You need to define a method similar to below.
public void addValueToList(Double d, List<Double> inputs , Map<String,Integer> map){
if( d == null ) return;
if ( isZero(d) ){
Integer count = map.get("zero");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("zero",count);
}else if( isNegative(d) ) {
Integer count = map.get("negative");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("negative",count);
}else {
Integer count = map.get("positive");
if(count == null){
count = 1;
}else {
count ++;
}
map.put("positive",count);
}
inputs.add(d);
}
For writing method for comparing the value you can refer https://stackoverflow.com/a/10400718/504133
Upvotes: 0
Reputation: 61178
Lets start with a quick tidy, lets create an enum
and remove those nasty String
constants that you don't have defined anywhere:
public enum Sign {
POSITIVE,
ZERO,
NEGATIVE;
public static Sign of(double d) {
if (d > 0) {
return POSITIVE;
}
if (d < 0) {
return NEGATIVE;
}
return ZERO;
}
}
Now we can trivially write a method to increment the appropriate value:
public void increment(final double d, final Map<Sign, Integer> count) {
count.merge(Sign.of(d), 1, Integer::sum);
}
For a quick test:
final Map<Sign, Integer> count = new EnumMap<>(Sign.class);
increment(0, count);
System.out.println(count);
increment(-1, count);
System.out.println(count);
increment(1, count);
System.out.println(count);
increment(-2, count);
System.out.println(count);
increment(2, count);
System.out.println(count);
Output:
{ZERO=1}
{NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=1, ZERO=1}
{POSITIVE=1, NEGATIVE=2, ZERO=1}
{POSITIVE=2, NEGATIVE=2, ZERO=1}
So how does this magic work? From the documentation for Map.merge
If the specified key is not already associated with a value or is associated with null, associates it with the given non-null value. Otherwise, replaces the associated value with the results of the given remapping function, or removes if the result is null. This method may be of use when combining multiple mapped values for a key.
So it takes the key
as the first argument to merge
- in the case Sign.of(d)
; this selects the correct bucket. If the value mapped to that key
is null
then it simply puts a mapping for the key
to the value
passed as the second argument - in this case 1
. Otherwise it gets a litte more complicated; it takes the value currently mapped to that key and uses the remappingFunction
passed as the third argument. This is a BiFunction<V,V,V>
, so it takes two arguments of type V
, the value
type and returns a single one - it merges the two together. Here we use Integer::sum
to take the existing value
, the new value
and return their sum
.
But we can go one step further, we can use a Stream
to carry this out on an arbitrary double[]
:
public Map<Sign, Long> count(final double[] d) {
return Arrays.stream(d)
.mapToObj(Sign::of)
.collect(groupingBy(identity(), () -> new EnumMap<>(Sign.class), counting()));
}
Note: I've used an EnumMap
here, which is Map
optimised for using an enum
as the key.
Upvotes: 2
Reputation: 4555
Map.put(k, v)
always overrides your previous value. You can use the "traditional approach":
if (!map.containsKey("positive"))
map.put("positive", 0);
map.put("positive", map.get("positive") + 1);
or better use the new merge
funtction that is added for exactly such cases:
map.merge("positive", 1, (prev, one) -> prev + one);
But this whole logic can be greatly shortened by using Math.signum()
and stream collectors:
Map<Double, Long> collect = inputs.stream()
.collect(Collectors.groupingBy(Math::signum,
Collectors.counting()));
System.out.println("positive: " + collect.get(1.0));
System.out.println("negative: " + collect.get(-1.0));
System.out.println("zero: " + collect.get(0.0));
Upvotes: 2
Reputation: 43699
You can solve it pretty concisely with streams. You need a function which turns value into the negative
/zero
/positive
key. Then just group by this key with a counting collector. It's basically a two-liner:
List<Double> values = Arrays.asList(-4.56,-4.66,0.0, 6.0, -6.99, 6.97);
Function<Double, String> toKey = v -> v < 0 ? "negative" : (v == 0 ? "zero" : "positive");
Map<String, Long> result = values
.stream()
.collect(Collectors
.groupingBy(
toKey,
Collectors.counting()));
Upvotes: 1