Java 8 Map merge VS compute, essential difference? - collections

It seems Both merge and compute Map methods are created to reduce if("~key exists here~") when put.
My problem is: add to map a [key, value] pair when I know nothing: neither key existing in map nor it exist but has value nor value == null nor key == null.
words.forEach(word ->
map.compute(word, (w, prev) -> prev != null ? prev + 1 : 1)
);
words.forEach(word ->
map.merge(word, 1, (prev, one) -> prev + one)
);
Is the only difference 1 is moved from Bifunction to parameter?
What is better to use? Does any of merge, compute suggests key/val are existing?
And what is essential difference in use case of them?

The documentation of Map#compute(K, BiFunction) says:
Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). For example, to either create or append a String msg to a value mapping:
map.compute(key, (k, v) -> (v == null) ? msg : v.concat(msg))
(Method merge() is often simpler to use for such purposes.)
If the remapping function returns null, the mapping is removed (or remains absent if initially absent). If the remapping function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.
The remapping function should not modify this map during computation.
And the documentation of Map#merge(K, V, BiFunction) says:
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. For example, to either create or append a String msg to a value mapping:
map.merge(key, msg, String::concat)
If the remapping function returns null, the mapping is removed. If the remapping function itself throws an (unchecked) exception, the exception is rethrown, and the current mapping is left unchanged.
The remapping function should not modify this map during computation.
The important differences are:
For compute(K, BiFunction<? super K, ? super V, ? extends V>):
The BiFunction is always invoked.
The BiFunction accepts the given key and the current value, if any, as arguments and returns a new value.
Meant for taking the key and current value (if any), performing an arbitrary computation, and returning the result. The computation may be a reduction operation (i.e. merge) but it doesn't have to be.
For merge(K, V, BiFunction<? super V, ? super V, ? extends V>):
The BiFunction is invoked only if the given key is already associated with a non-null value.
The BiFunction accepts the current value and the given value as arguments and returns a new value. Unlike with compute, the BiFunction is not given the key.
Meant for taking two values and reducing them into a single value.

If the mapping function, as in your case, only depends on the current mapped value, then you can use both. But I would prefer:
compute if you can guarantee that a value for the given key exists. In this case the extra value parameter taken by the merge method is not needed.
merge if it is possible that no value for the given key exists. In this case merge has the advantage that null does NOT have to be handled by the mapping function.

Related

Swiftui: how do you assign the value in a "String?" object to a "String" object?

Swiftui dictionaries have the feature that the value returned by using key access is always of type "optional". For example, a dictionary that has type String keys and type String values is tricky to access because each returned value is of type optional.
An obvious need is to assign x=myDictionary[key] where you are trying to get the String of the dictionary "value" into the String variable x.
Well this is tricky because the String value is always returned as an Optional String, usually identified as type String?.
So how is it possible to convert the String?-type value returned by the dictionary access into a plain String-type that can be assigned to a plain String-type variable?
I guess the problem is that there is no way to know for sure that there exists a dictionary value for the key. The key used to access the dictionary could be anything so somehow you have to deal with that.
As described in #jnpdx answer to this SO question (How do you assign a String?-type object to a String-type variable?), there are at least three ways to convert a String? to a String:
import SwiftUI
var x: Double? = 6.0
var a = 2.0
if x != nil {
a = x!
}
if let b = x {
a = x!
}
a = x ?? 0.0
Two key concepts:
Check the optional to see if it is nil
if the optional is not equal to nil, then go ahead
In the first method above, "if x != nil" explicitly checks to make sure x is not nil be fore the closure is executed.
In the second method above, "if let a = b" will execute the closure as long as b is not equal to nil.
In the third method above, the "nil-coalescing" operator ?? is employed. If x=nil, then the default value after ?? is assigned to a.
The above code will run in a playground.
Besides the three methods above, there is at least one other method using "guard let" but I am uncertain of the syntax.
I believe that the three above methods also apply to variables other than String? and String.

Runtime error:dictionary changed size during iteration

I iterate thru items of a dictionary "var_dict".
Then as I iterate in a for loop, I need to update the dictionary.
I understand that is not possible and that triggers the runtime error I experienced.
My question is, do I need to create a different dictionary to store data? As is now, I am trying to use same dictionary with different keys.
I know the problem is related to iteration thru the key and values of a dictionary and attempt to change it. I want to know if the best option in this case if to create a separate dictionary.
for k, v in var_dict.items():
match = str(match)
match = match.strip("[]")
match = match.strip("&apos;&apos;")
result = [index for index, value in enumerate(v) if match in value]
result = str(result)
result = result.strip("[]")
result = result.strip("&apos;")
#====> IF I print(var_dict), at this point I have no error *********
if result == "0":
#It means a match between interface on RP PSE2 model was found; Interface position is on PSE2 architecture
print (f&apos;PSE-2 Line cards:{v} Interfaces on PSE2:{entry} Interface PortID:{port_id}&apos;)
port_id = int(port_id)
print(port_id)
if port_id >= 19:
#print(f&apos;interface:{entry} portID={port_id} CPU_POS={port_cpu_pos} REPLICATION=YES&apos;)
if_info = [entry,&apos;PSE2=YES&apos;,port_id,port_cpu_pos,&apos;REPLICATION=YES&apos;]
var_dict[&apos;IF_PSE2&apos;].append(if_info)
#===> *** This is the point that if i attempt to print var_dict, I get the Error during olist(): dictionary changed size during iteration
else:
#print(f&apos;interface:{entry},portID={port_id} CPU_POS={port_cpu_pos} REPLICATION=NO&apos;)
if_info = [entry,&apos;PSE2=YES&apos;,port_id,port_cpu_pos,&apos;REPLICATION=NO&apos;]
var_dict[&apos;IF_PSE2&apos;].append(if_info)
else:
#it means the interface is on single PSE. No replication is applicable. Just check threshold between incoming and outgoing rate.
if_info = [entry,&apos;PSE2=NO&apos;,int(port_id),port_cpu_pos,&apos;REPLICATION=NO&apos;]
var_dict[&apos;IF_PSE1&apos;].append(if_info)
I did a shallow copy and that allowed me to iterate a dictionary copy and make modifications to the original dictionary. Problem solved. Thanks.
(...)
temp_var_dict = var_dict.copy()
for k, v in temp_var_dict.items():
(...)

the relation between add key/value and mapassign method in go1.10.3

I am reading the source code of map in go1.10.3.It seemed there exist corresponding method about operation such as:
makemap(t *maptype, hint int, h *hmap) *hmap ==> m = make(map[xx]yy)
mapaccess1(t *maptype, h *hmap, key unsafe.Pointer)==> m['key']
but I cant find the correspond method for the operation which add key/value as below:
m['xx']='yy'
there exist a method called mapassign which has some similarity with this
operation.
mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer
this will add a new key to the map, but as we can see, the input arguments has no value. And another question is when it has already this key, it maybe update this key.
if !alg.equal(key, k) {
continue
}
// already have a mapping for key. Update it.
if t.needkeyupdate {//why??
typedmemmove(t.key, k, key)
}
since the two key is equal, why should update it?
summary:
1. the relation between add key/value operation and method mapassign?
2. why it maybe need to update the key since the insert key and the key which has already exist is equal in mapassign method?
In the operation m[k] = v, the caller copies the value v to the address returned by mapassign.
The comments in the function needkeyupdate explain why some types need key updates: floating point & complex -0 and 0 are equal, but different values; string might have smaller backing store.

"cannot take the address of" and "cannot call pointer method on"

This compiles and works:
diff := projected.Minus(c.Origin)
dir := diff.Normalize()
This does not (yields the errors in the title):
dir := projected.Minus(c.Origin).Normalize()
Can someone help me understand why? (learning Go)
Here are those methods:
// Minus subtracts another vector from this one
func (a *Vector3) Minus(b Vector3) Vector3 {
return Vector3{a.X - b.X, a.Y - b.Y, a.Z - b.Z}
}
// Normalize makes the vector of length 1
func (a *Vector3) Normalize() Vector3 {
d := a.Length()
return Vector3{a.X / d, a.Y / d, a.Z / d}
}
The Vector3.Normalize() method has a pointer receiver, so in order to call this method, a pointer to Vector3 value is required (*Vector3). In your first example you store the return value of Vector3.Minus() in a variable, which will be of type Vector3.
Variables in Go are addressable, and when you write diff.Normalize(), this is a shortcut, and the compiler will automatically take the address of the diff variable to have the required receiver value of type *Vector3 in order to call Normalize(). So the compiler will "transform" it to
(&diff).Normalize()
This is detailed in Spec: Calls:
A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m().
The reason why your second example doesn't work is because return values of function and method calls are not addressable, so the compiler is not able to do the same here, the compiler is not able to take the address of the return value of the Vector3.Minus() call.
What is addressable is exactly listed in the Spec: Address operators:
The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x [in the expression of &x] may also be a (possibly parenthesized) composite literal.
See related questions:
How to get the pointer of return value from function call?
How can I store reference to the result of an operation in Go?
Possible "workarounds"
"Easiest" (requiring the least change) is simply to assign to a variable, and call the method after that. This is your first working solution.
Another way is to modify the methods to have a value receiver (instead of pointer receiver), so that there is no need to take the address of the return values of the methods, so calls can be "chained". Note that this might not be viable if a method needs to modify the receiver, as that is only possible if it is a pointer (as the receiver is passed just like any other parameters – by making a copy –, and if it's not a pointer, you could only modify the copy).
Another way is to modify the return values to return pointers (*Vector3) instead of Vector3. If the return value is already a pointer, no need to take its address as it's good as-is for the receiver to a method that requires a pointer receiver.
You may also create a simple helper function which returns its address. It could look something like this:
func pv(v Vector3) *Vector3 {
return &v
}
Using it:
dir := pv(projected.Minus(c.Origin)).Normalize()
This could also be a method of Vector3, e.g.:
func (v Vector3) pv() *Vector3 {
return &v
}
And then using it:
dir := projected.Minus(c.Origin).pv().Normalize()
Some notes:
If your type consists of 3 float64 values only, you should not see significant performance differences. But you should be consistent about your receiver and result types. If most of your methods have pointer receivers, so should all of them. If most of your methods return pointers, so should all of them.
The accepted answer is really long so I'm just going to post what helped me:
I got this error regarding this line:
services.HashingServices{}.Hash("blabla")
so I just changed it to:
(&services.HashingServices{}).Hash("blabla")

Why _.map iteratee's arguments is (value, key) not (key, value)?

According to the doc,
If list is a JavaScript object, iteratee's arguments will be (value, key, list).
I constantly have to check the doc to verify the order. Why is value, key not key, value?
[EDIT]
I guess I'm (always) confused because the for loop in CoffeeScript iterates on key, value:
yearsOld = max: 10, ida: 9, tim: 11
ages = for child, age of yearsOld
"#{child} is #{age}"
Because the value is the more important, and most generic part of mapping over structures. Maybe not so much with _.map over objects, but when you map over arrays you typically use a unary function (which takes only the value). The index (or key) is hardly ever used, so it became the second argument that is usually omitted from the parameter list.

Resources