Say I have the following map:
Map<Member, List<Message>> messages = ... //constructed somehow
I would like to use the java 8 stream api in order to obtain a:
SortedMap<Message, Member> latestMessages = ...
Where the comparator passed into the SortedMap/TreeMap would be based on the message sendDate field.
Furthermore, of the list of sent messages, I would select the latest message which would become the key to the sorted map.
How can I achieve that?
edit 1:
Comparator<Message> bySendDate = Comparator.comparing(Message::getSendDate);
SortedMap<Message, Member> latestMessages = third.entrySet().stream()
.collect(Collectors.toMap(e -> e.getValue().stream().max(bySendDate).get(), Map.Entry::getKey, (x, y) -> {
throw new AssertionError();
}, () -> new TreeMap(bySendDate.thenComparing(Comparator.comparing(Message::getId)))));
I get the following compilation error:
The method collect(Collector<? super T,A,R>) in the type Stream<T> is not applicable for the arguments (Collector<Map.Entry<Member,List<Message>>,?,TreeMap>)
Let’s dissolve this into two parts.
First, transform Map<Member, List<Message>> messages into a Map<Message, Member> latestMessages by reducing the messages for a particular communication partner (Member) to the latest:
Map<Message, Member> latestMessages0 = messages.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getValue().stream().max(Comparator.comparing(Message::getSendDate)).get(),
Map.Entry::getKey));
Here, the resulting map isn’t sorted but each mapping will contain the latest message shared with that participant.
Second, if you want to have the resulting map sorted by sendDate, you have to add another secondary sort criteria to avoid losing Messages which happen to have the same date. Assuming that you have a Long ID that is unique, adding this ID as secondary sort criteria for messages with the same date would be sufficient:
Comparator<Message> bySendDate=Comparator.comparing(Message::getSendDate);
SortedMap<Message, Member> latestMessages = messages.entrySet().stream()
.collect(Collectors.toMap(
e -> e.getValue().stream().max(bySendDate).get(),
Map.Entry::getKey, (x,y) -> {throw new AssertionError();},
()->new TreeMap<>(bySendDate.thenComparing(Comparator.comparing(Message::getId)))));
Since sorting by the unique IDs should solve any ambiguity, I provided a merge function which will unconditionally throw, as calling it should never be required.
Related
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.
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("''")
result = [index for index, value in enumerate(v) if match in value]
result = str(result)
result = result.strip("[]")
result = result.strip("'")
#====> 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'PSE-2 Line cards:{v} Interfaces on PSE2:{entry} Interface PortID:{port_id}')
port_id = int(port_id)
print(port_id)
if port_id >= 19:
#print(f'interface:{entry} portID={port_id} CPU_POS={port_cpu_pos} REPLICATION=YES')
if_info = [entry,'PSE2=YES',port_id,port_cpu_pos,'REPLICATION=YES']
var_dict['IF_PSE2'].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'interface:{entry},portID={port_id} CPU_POS={port_cpu_pos} REPLICATION=NO')
if_info = [entry,'PSE2=YES',port_id,port_cpu_pos,'REPLICATION=NO']
var_dict['IF_PSE2'].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,'PSE2=NO',int(port_id),port_cpu_pos,'REPLICATION=NO']
var_dict['IF_PSE1'].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():
(...)
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.
I'm messing around a bit with F# and I'm not quite sure if I'm doing this correctly. In C# this could be done with an IDictionary or something similar.
type School() =
member val Roster = Map.empty with get, set
member this.add(grade: int, studentName: string) =
match this.Roster.ContainsKey(grade) with
| true -> // Can I do something like this.Roster.[grade].Insert([studentName])?
| false -> this.Roster <- this.Roster.Add(grade, [studentName])
Is there a way to insert into the map if it contains a specified key or am I just using the wrong collection in this case?
The F# Map type is a mapping from keys to values just like ordinary .NET Dictionary, except that it is immutable.
If I understand your aim correctly, you're trying to keep a list of students for each grade. The type in that case is a map from integers to lists of names, i.e. Map<int, string list>.
The Add operation on the map actually either adds or replaces an element, so I think that's the operation you want in the false case. In the true case, you need to get the current list, append the new student and then replace the existing record. One way to do this is to write something like:
type School() =
member val Roster = Map.empty with get, set
member this.Add(grade: int, studentName: string) =
// Try to get the current list of students for a given 'grade'
let studentsOpt = this.Roster.TryFind(grade)
// If the result was 'None', then use empty list as the default
let students = defaultArg studentsOpt []
// Create a new list with the new student at the front
let newStudents = studentName::students
// Create & save map with new/replaced mapping for 'grade'
this.Roster <- this.Roster.Add(grade, newStudents)
This is not thread-safe (because calling Add concurrently might not update the map properly). However, you can access school.Roster at any time, iterate over it (or share references to it) safely, because it is an immutable structure. However, if you do not care about that, then using standard Dictionary would be perfectly fine too - depends on your actual use case.
I have made a Map which includes several Dictionaries. Everytime I receive a data, I will find the corresponding dictionary in the Map, and then add new information in this dictionary.
But the problem is every time I try to add information , it won't add it only in the corresponding dictionary, instead it will add it into all the dictionaries in the map.
please , i am becoming crazy.
while datareceive do
let refdictionary = ref totalmap.[index] //totalmap has a lot of Dictionary, which is indexed by "index"
let dictionnarydata = totalmap.[index]
if dictionnarydata.ContainsKey(key1) then
........
else
refdic.Value.Add(key1,num) //if the corresponding dictionary does not have such information, then add it in it
()
As mentioned in the comments, if you are learning functional programming, then the best approach is to use immutable data structures - here, you could use a map that maps the index to a nested map (which contains the key value information that you need).
Try playing with something like the following sample:
// Add new item (key, num pair) to the map at the specified index
// Since totalMap is immutable, this returns a new map!
let addData index (key:int) (num:int) (totalmap:Map<_, Map<_, _>>) =
// We are assuming that the value for index is defined
let atIndex = totalmap.[index]
let newAtIndex =
// Ignore information if it is already there, otherwise add
if atIndex.ContainsKey key then atIndex
else atIndex.Add(key, num)
// Using the fact that Add replaces existing items, we
// can just add new map in place of the old one
totalmap.Add(index, newAtIndex)
Using the above function, you can now create initial map and then add various information to it:
// Create an int-indexed map containing empty maps as values
let totalmap = Map.ofSeq [ for i in 0 .. 10 -> i, Map.empty ]
totalmap
|> addData 0 1 42
|> addData 0 1 32
|> addData 1 10 1