# A global hash table where values are sets

**URL:** <https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677>\
**Category:** General\
**Tags:** question\
**Created:** [January 23, 2024, 3:51pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677 "2024-01-23T15:51:27Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![jkleiser](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jkleiser/32/498_2.png) [@jkleiser](https://racket.discourse.group/u/jkleiser)\
**Post date:** [January 23, 2024, 3:51pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/1 "2024-01-23T15:51:27Z")

</div>

I have a text file of words, and I have found out how to read it, line by line. I extract the words from each line into a list, and those lists look like this: (w0 (w10 w11 w12 ...) (w20 w21 w22 ...))  
As I read new lines, I would like to add words to a global hash table, let's call it wh. For each w0, I want to make sure that w0 is in wh; if it's not, I must insert it with its value being an empty hash set. Now that I know w0 is in wh, I shall add w20, w21, ... to the hash set belonging to w0 in wh.  
If you can help me find out how to do this, then I shall be able to take care of w10, w11, ...  
I'm looking forward to your suggestions. Thanks.

---

<div class="post-metadata">

**Author:** ![scolobb](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/scolobb/32/108_2.png) [@scolobb](https://racket.discourse.group/u/scolobb)\
**Post date:** [January 23, 2024, 4:33pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/2 "2024-01-23T16:33:45Z")

</div>

Hi @jkleiser ,

As a first approach, I would use [hash-has-key?](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._hash-has-key~3f%29%29) to check whether `wh` already contains `w0` or not. If not, I would just use [hash-set!](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28quote._~23~25kernel%29._hash-set%21%29%29) to add `w0` to `wh`, mapped to the the empty set. If `wh` already contains `w0`, I would first use [hash-ref](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28quote._~23~25kernel%29._hash-ref%29%29) to first retrieve the set `w0` is mapped to in `wh`, and then use `hash-set!` to update it to the union of the new set with the old set.

Depending on the size of the files you process, you might also use [group-by](https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28lib._racket%2Flist..rkt%29._group-by%29%29) to first group together all the lists to which `w0` should be mapped, and then append then together and convert the result to a set.

There are probably faster way to achieve what you need by taking into consideration the performance aspects of the used functions, but I hope my suggestions will help you get started.

---

<div class="post-metadata">

**Author:** ![soegaard](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/soegaard/32/19_2.png) [@soegaard](https://racket.discourse.group/u/soegaard)\
**Post date:** [January 23, 2024, 5:32pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/3 "2024-01-23T17:32:46Z")

</div>

Something like this?

```scheme
#lang racket

(define ht (make-hash))

(define (insert w0 ws)
  (define w0-words (hash-ref! ht w0 (λ () '())))
  (hash-set! ht w0 (append ws w0-words)))

(insert "foo" (list "foo1" "foo2"))
(insert "bar" (list "bar1" "bar2"))
(insert "foo" (list "foo3" "foo4"))

(hash-ref ht "foo")

```

---

<div class="post-metadata">

**Author:** ![jkleiser](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jkleiser/32/498_2.png) [@jkleiser](https://racket.discourse.group/u/jkleiser)\
**Post date:** [January 23, 2024, 8:25pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/4 "2024-01-23T20:25:38Z")

</div>

Thanks to both of you for getting me on track! I used soegaard's 'insert' definition, and it worked great.  
In my little job, speed is not important at all, but thanks for telling me of the possibilities for speed-up.

---

<div class="post-metadata">

**Author:** ![jkleiser](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jkleiser/32/498_2.png) [@jkleiser](https://racket.discourse.group/u/jkleiser)\
**Post date:** [January 24, 2024, 8:01pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/5 "2024-01-24T20:01:14Z")

</div>

After some testing, I found that soegaard's 'insert' for a given w0 could result in a list where some words could be repeated. That would happen if the last insert-line in his example was  
(insert "foo" (list "foo3" "foo4" "foo1")

Here is my new version:

```scheme
(define (insert w0 ws)
  (define w0-set (hash-ref! all-words w0 (λ () (mutable-set))))
  (for-each (λ (w)
              (set-add! w0-set w))
            ws)
  (hash-set! all-words w0 w0-set))

```

Maybe not as elegant, but it eleminates duplicates. (My 'all-words' is his 'ht'.)

---

<div class="post-metadata">

**Author:** ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)\
**Post date:** [January 27, 2024, 8:55pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/6 "2024-01-27T20:55:11Z")

</div>

You can simplify things by using [`hash-update!`](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._hash-update%21%29%29) to combine looking up the key and updating its value into one step:

```scheme
(define (insert w0 ws)
  (hash-update! all-words w0
                (lambda (w-set) (for ([w (in-list ws)]) (set-add! w-set w)) w-set)
                mutable-set))

```

or using immutable sets instead:

```scheme
(define (insert w0 ws)
  (hash-update! all-words w0
                (lambda (w-set) (foldl (lambda (w s) (set-add s w)) w-set ws))
                set))

```

---

<div class="post-metadata">

**Author:** ![jkleiser](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jkleiser/32/498_2.png) [@jkleiser](https://racket.discourse.group/u/jkleiser)\
**Post date:** [January 28, 2024, 7:47pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/7 "2024-01-28T19:47:28Z")

</div>

Thanks a lot, shawnw! hash-update! was a nice solution.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [January 28, 2024, 8:16pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/8 "2024-01-28T20:16:41Z")

</div>

Nice! I did not know about hash-update. I mean, I could have implemented it, I suppose, but now that I know about it I'll use it all the time! Also, should your second example have used `hash-update` instead of `hash-update!` ?

---

<div class="post-metadata">

**Author:** ![shawnw](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/shawnw/32/1031_2.png) [@shawnw](https://racket.discourse.group/u/shawnw)\
**Post date:** [January 28, 2024, 9:23pm UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/9 "2024-01-28T21:23:55Z")

</div>

The second one is still assuming a mutable hash table, so `hash-update!` it is.

---

<div class="post-metadata">

**Author:** ![jbclements](https://yyz2.discourse-cdn.com/free1/user_avatar/racket.discourse.group/jbclements/32/11_2.png) [@jbclements](https://racket.discourse.group/u/jbclements)\
**Post date:** [February 1, 2024, 4:56am UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/10 "2024-02-01T04:56:19Z")

</div>

Oh! Sorry, I missed that.

---

<div class="post-metadata">

**Author:** ![dstorrs](https://avatars.discourse-cdn.com/v4/letter/d/898d66/32.png) [@dstorrs](https://racket.discourse.group/u/dstorrs)\
**Post date:** [April 23, 2024, 3:17am UTC](https://racket.discourse.group/t/a-global-hash-table-where-values-are-sets/2677/11 "2024-04-23T03:17:11Z")

</div>

One thing to note: `hash-update!` is essentially a `hash-ref` followed by a `hash-set!`, meaning that you need to worry about thread interactions.

> The [hash-update!](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._hash-update%21%29%29) and [hash-ref!](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28lib._racket%2Fprivate%2Fmore-scheme..rkt%29._hash-ref%21%29%29) functions use a table’s semaphore independently for the [hash-ref](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28quote._~23~25kernel%29._hash-ref%29%29) and [hash-set!](https://docs.racket-lang.org/reference/hashtables.html#%28def._%28%28quote._~23~25kernel%29._hash-set%21%29%29) parts of their functionality, which means that the update as a whole is not “atomic.”

Quoted from [4.15&nbsp;Hash Tables](https://docs.racket-lang.org/reference/hashtables.html#%28elem._%28caveat._concurrency%29%29), which has more caveats to offer regarding concurrent modification.
