0

I have a map of type Map> , I need to add values to the Set multiple times :

Fields = new Map<string, Set<string>>();

 this.array1.forEach(s => this.Fields.set(`${s.id}`, Utils1.otherFields(s)));
 this.array2.forEach(s => this.Fields.set(`${s.id}`, Utils2.otherFields2(s)));

now the loop does the job perfectly , but when I return the Map , it only has the last values set in it , which is in this case values from otherFields2.

what am I doing wrong ?

2
  • That's because you're overwriting the Set with new data from Utils1.otherFields(s) or Utils2.otherFields(s) at every iteration. You will need to merge the sets returned in the map, instead of reassigning/overwriting them. Commented Nov 14, 2019 at 15:44
  • thank you @Terry , any idea on how to do that ? Commented Nov 14, 2019 at 15:46

1 Answer 1

2

JavaScript doesn't have a lot of built-in support for manipulating Maps and Sets, so you'll have to write this yourself. Here's a possible way to do this.

First, let's introduce the Map utility function computeIfAbsent(), (inspired by a Java Map method of the same name) which takes a map and a key, and a callback function to compute a default value for that key. It behaves sort of like a map.get(key) which is guaranteed to return a result. If the map has a value for that key, you get it. Otherwise, the callback is called to make a value, and that value is put into the map before it's returned to you:

function computeIfAbsent<K, V>(map: Map<K, V>, key: K, mappingFunction: (key: K) => V): V {
    let val = map.get(key);
    if (typeof val === "undefined") {
        val = mappingFunction(key);
        map.set(key, val);
    }
    return val;
}

Then we introduce the Set utility function addAll(), (inspired by a Java Set method of the same name) which takes a set and a collection of values, and adds all the values from the collection into the set:

function addAll<V>(s: Set<V>, other: Iterable<V>) {
    for (const o of other) s.add(o);
}

Armed with both of those, your code should be changed to something like this:

    this.array1.forEach(s => addAll(
        computeIfAbsent(this.Fields, `${s.id}`, () => new Set()),
        Utils1.otherFields(s)
    ));
    this.array2.forEach(s => addAll(
        computeIfAbsent(this.Fields, `${s.id}`, () => new Set()),
        Utils2.otherFields2(s)
    ));

The idea is that for each element s in the array, you get the Set<string> corresponding to the s.id key in the Fields map, or create a new empty one if it isn't there. Then, to this set you add all the values from your Utils method. This should have the effect of merging instead of overwriting.

Okay, hope that helps; good luck!

Link to code

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you @jcalz for your detailed answer. I have one problem with this solution , is that if there are maps with similar keys , it leaves just one , not both of them , I need them to be both added in my result.
it means that I don't need the computeIfAbsent() method ?
What do you mean by "maps with similar keys"? In your example code there is only one map, named Fields. What do you mean by "both added in my result"? The code I present merges Sets into new Sets. If you want to "add" them in some other way, let me know. Ideally you should provide a minimal reproducible example that shows exactly what you expect the inputs and outputs to be. Please read the description of what constitutes a minimal reproducible example. Good luck!
thank you , I mis-explained in my comment , your code work perfectly fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.