# Retrieve element from a set

**URL:** https://discourse.julialang.org/t/retrieve-element-from-a-set/105658
**Category:** General Usage
**Tags:** question
**Created:** [November 1, 2023, 3:19pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658 "2023-11-01T15:19:17Z")
**Posts on this page:** 10
**Page:** 1

<div class="post-metadata">

### Author: ![odddot](https://avatars.discourse-cdn.com/v4/letter/o/ecd19e/32.png) [@odddot](https://discourse.julialang.org/u/odddot)
#### Post date: [November 1, 2023, 3:19pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/1 "2023-11-01T15:19:17Z")

</div>

Hi,

I want to do something that may be impossible to do. It is not very important (I have possible workarounds), but I am curious…

I have a struct that represents an edge, defined as:

```julia
struct Edge{I<Integer}
    e::Tuple{I,I}
    p::I
    r::Bool
end

```

The idea is that the actual edge is represented by the tuple `e`, whereas `p` and `r` are some attributes of the edge that are only relevant in some particular situations. I want to store Edges in a `Set`, but I want to be able to check set membership using only the tuple `e`. For this I redefine `Base.hash`, `Base.isequal` and `Base.==`:

```julia
    import Base: hash, isequal, ==
    hash(edge::Edge) = hash(edge.e)
    isequal(e1::Edge,e2::Edge) = isequal(e1.e,e2.e)
    ==(e1::Edge,e2::Edge) = ==(e1.e,e2.e)

```

With this, I can do something like:

```julia
    a = Edge((1,2),2,true); b = Edge((1,2),3,false); c = Edge((1,3),1,true);
    S = Set([a,b,c]) 
    (1,3) in S

```

and I get what I want. Namely: when constructing `S`, `b` replaces `a` since they are the same edge, and the answer to `(1,3) in S` is `true`. So far, so good.

However, in this way the attributes of the edges are inaccesible: I can check if the edge belongs to `S` but not what attributes are attached to it. Essentially, I cannot _extract_ and `Edge` as it is stored in `S`. I can, for example, use `pop!(S,m)` where `m` is either a tuple or an `Edge` build from the tuple and default values for `p` and `r`. This removes the corresponding `Edge` from `S`, but the output is the same `m` that I used as key for popping, so the attributes of the previously stored edge are lost.

A simple solution is to use a dictionary instead of a set. In particular, a `Dict{Tuple{I,I},Tuple{I,Bool}}`, with the tuples as keys and the attributes as values. But in this way I essentially drop the data type `Edge` that is useful in other parts of the code.  
It is not a great deal: I can keep `Edge` for the things it is useful, and just store my _set of edges_ as a `Dict` instead of a real `Set` of `Edge`s. But I wonder if there is some trick that I can apply for having my cake and eating it. In other words: Given a set where the `hash` applies not the whole element but only to a part, is it possible to retrieve the complete element?

Thanks!

---

<div class="post-metadata">

### Author: ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)
#### Post date: [November 1, 2023, 3:27pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/2 "2023-11-01T15:27:45Z")

</div>

This is not possible using only stable API and with the existing `Set` type. I would advice you use the Dict approach.  
For fun, it’s possible to do this by relying on the internal implementation of `Set`:

```julia
julia> s = Set([1,2,3]);

julia> function get_element(s::Set, v)
           i = Base.ht_keyindex2!(s.dict, v)
           i < 1 ? nothing : s.dict.keys[i]
       end
get_element (generic function with 1 method)

julia> get_element(s, 0x02)
2

```

Note that this implementation may break in any future Julia release.

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [November 1, 2023, 3:38pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/3 "2023-11-01T15:38:30Z")

</div>

> [@odddot](#):
>
> I can, for example, use `pop!(S,m)` where `m` is either a tuple or an `Edge` build from the tuple and default values for `p` and `r`. This removes the corresponding `Edge` from `S`, but the output is the same `m` that I used as key for popping, so the attributes of the previously stored edge are lost.

Hunh. Is this a bug? What why wouldn’t we return the value as it appears in the set?

> #### `pop!(collection, key[, default])`
> 
> Delete and return the mapping for `key` if it exists in `collection`…

I suppose it’s not _quite_ clear what “the mapping for `key`” would be in a Set, but it seems defensible that it should be the value in the set itself.

```julia-repl
julia> s = Set((1,2,3))
Set{Int64} with 3 elements:
  2
  3
  1

julia> pop!(s, 1.0)
1.0

```

---

<div class="post-metadata">

### Author: ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)
#### Post date: [November 1, 2023, 3:56pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/4 "2023-11-01T15:56:58Z")

</div>

> [@odddot](#):
>
> use `pop!(S,m)` where `m` is either a tuple or an `Edge` build from the tuple and default values for `p` and `r`.

the expression

```julia
julia> m=(1,3)
(1, 3)

julia> pop!(S,m)
ERROR: KeyError: key (1, 3) not found

```

gives me an error

Maybe this might be for you

```julia
[e for e in S if e.e==(1,3)]
[e for e in S if e==Edge{Int64}((1, 3), 991, true)]

```

---

<div class="post-metadata">

### Author: ![odddot](https://avatars.discourse-cdn.com/v4/letter/o/ecd19e/32.png) [@odddot](https://discourse.julialang.org/u/odddot)
#### Post date: [November 1, 2023, 4:40pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/5 "2023-11-01T16:40:01Z")

</div>

Aha!  
Nice trick. I had not noticed that a `Set` is actually a `Dict` with values set to `nothing`. Following your approach, it is possible to use `getkey`:

```julia
julia> S = Set([1,2,3]);

julia> getkey(S.dict,0x02,nothing)
2

```

Thanks!

---

<div class="post-metadata">

### Author: ![odddot](https://avatars.discourse-cdn.com/v4/letter/o/ecd19e/32.png) [@odddot](https://discourse.julialang.org/u/odddot)
#### Post date: [November 1, 2023, 4:47pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/6 "2023-11-01T16:47:38Z")

</div>

Indeed! My reasoning was that `pop!` should return the value as stored in the `Set`. That’s why I though that my approach was more or less acceptable.  
Thanks!

---

<div class="post-metadata">

### Author: ![odddot](https://avatars.discourse-cdn.com/v4/letter/o/ecd19e/32.png) [@odddot](https://discourse.julialang.org/u/odddot)
#### Post date: [November 1, 2023, 4:52pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/7 "2023-11-01T16:52:22Z")

</div>

You are right. In order to use `pop!(S,(1,3))` it is necessary to define a method for comparing tuples and edges:

```julia
import Base.==
==(t::Tuple{I,I},e::Edge) where I<:Integer = ==(t,e.e)

```

The problem with `[e for e in S if e.e==(1,3)]` is that you actually check the whole set, which is something I want to avoid.  
Thanks!

---

<div class="post-metadata">

### Author: ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)
#### Post date: [November 1, 2023, 6:01pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/8 "2023-11-01T18:01:10Z")

</div>

You could also define a personal _‘Set’_ with `values` equal to the `keys` instead of `nothing` as follows

```julia
julia> D=Dict([(k,k) for k in (a,b,c)])
Dict{Edge{Int64}, Edge{Int64}} with 2 entries:
  Edge{Int64}((1, 2), … => Edge{Int64}((1, 2), 3, false)
  Edge{Int64}((1, 3), … => Edge{Int64}((1, 3), 1, true) 

julia> D[a]
Edge{Int64}((1, 2), 3, false)

julia> D[b]
Edge{Int64}((1, 2), 3, false)

julia> D[c]
Edge{Int64}((1, 3), 1, true)

julia> D[(1,2)]
Edge{Int64}((1, 2), 3, false)

```

---

<div class="post-metadata">

### Author: ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)
#### Post date: [November 1, 2023, 6:04pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/9 "2023-11-01T18:04:35Z")

</div>

> [@mbauman](#):
>
> it’s not _quite_ clear what “the mapping for `key`” would be in a Set

Should it be nothing!?

Or is nothing the S.dict mapping!?

---

<div class="post-metadata">

### Author: ![mbauman](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mbauman/32/31082_2.png) [@mbauman](https://discourse.julialang.org/u/mbauman)
#### Post date: [November 8, 2023, 3:38pm UTC](https://discourse.julialang.org/t/retrieve-element-from-a-set/105658/10 "2023-11-08T15:38:51Z")

</div>

Thanks to @jakobnissen, `pop!` on a Set will begin returning the element removed in v1.11!

> <https://github.com/JuliaLang/julia/pull/52017>
>
> Previously, \`pop!(::Set, x)\` returned \`x\`, not the element in the set.This matte…rs if multiple different elements are equal and hash to the same.
> 
> I believe this might have been an oversight. It was not tested for, and it makes most sense to me that \`pop!\` returns the element actually in the set.
> 
> 
> Before:
> \`\`\`julia
> julia\> pop!(Set(\[1\]), 0x01)
> 0x01
> \`\`\`
> Now:
> \`\`\`julia
> julia\> pop!(Set(\[1\]), 0x01)
> 1
> \`\`\`
