# Map over namedtuple keys and values: confusing error

**URL:** <https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866>\
**Category:** General Usage\
**Created:** [July 27, 2019, 12:19am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866 "2019-07-27T00:19:42Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [July 27, 2019, 12:19am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/1 "2019-07-27T00:19:42Z")

</div>

Hi! I want to map a function, which gets a `Pair`, over both keys and values of a namedtuple, and the most obvious way seems to be using, well, `pairs`:

```julia
f(p) = (p.first, p.second)
nt = (A=5, B=6)

pairs(nt) |> collect
2-element Array{Pair{Symbol,Int64},1}:
 :A => 5
 :B => 6
# looks exactly what I want

pairs(nt) |> first |> f
(:A, 5)
# applying to the first element works, so map should also work - right?

map(f, pairs(nt))
map is not defined on dictionaries
Stacktrace:
 [1] error(::String) at ./error.jl:33
 [2] map(::Function, ::Base.Iterators.Pairs{Symbol,Int64,Tuple{Symbol,Symbol},NamedTuple{(:A, :B),Tuple{Int64,Int64}}}) at ./abstractarray.jl:2101
 [3] top-level scope at In[42]:1

```

Not sure why it fails, error message makes no sense to me.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [July 27, 2019, 6:20am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/2 "2019-07-27T06:20:27Z")

</div>

The error message tells you that this is deliberately undefined. The reason for this is that mapping the values, and keeping the keys (ie returning a `NamedTuple{(:A,:B)}` is also “reasonable” behavior, and at this point it is undecided whether this is preferable.

Erroring now allows this to be defined in a later Julia version without making it a breaking change.

---

<div class="post-metadata">

**Author:** ![zgornel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zgornel/32/217487_2.png) [@zgornel](https://discourse.julialang.org/u/zgornel)\
**Post date:** [July 27, 2019, 2:29pm UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/3 "2019-07-27T14:29:51Z")

</div>

`map(f, collect(pairs(nt)))` works.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [July 27, 2019, 6:26pm UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/4 "2019-07-27T18:26:57Z")

</div>

The thing is that error message is about dictionaries, and I don’t pass neither `Dict` nor a `NamedTuple` to `map` - it’s just an plain iterable of pairs.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [September 23, 2019, 5:57am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/5 "2019-09-23T05:57:10Z")

</div>

Bumping this thread - so there is indeed no way to map or broadcast over `pairs(...)` (iterable of pairs) without `collect`ing this iterable into an array?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [September 23, 2019, 6:22am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/6 "2019-09-23T06:22:14Z")

</div>

There a multiple ways of mapping a `NamedTuple`, depending on whether you want the result to be

1. inferred,
2. also `NamedTuple`,
3. with the same keys, or different ones (ie whether those are mapped, too).

Please provide **an MWE, with the desired input and output,** for your specific use case.

> [@Please read: make it easier to help you](https://discourse.julialang.org/t/psa-make-it-easier-to-help-you/14757):
>
> Welcome to the Julia Discourse! We are enthusiastic about helping Julia programmers, both beginner and experienced. This public service announcement (PSA) outlines best practices when asking for help. Following these points makes it easier for us to help you and more likely you’ll get a prompt, useful answer. Keywords are highlighted to make it easier to refer to specific points. Choose a descriptive title that captures the key part of your question, eg “plots with multiple axes” instead of …

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [September 23, 2019, 6:36am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/7 "2019-09-23T06:36:19Z")

</div>

Below is what I would expect from mapping/broadcasting on iterable of pairs - note that there is no mapping over a namedtuple or dictionary here, even though the error says that.

```julia
    f = x::Pair -> (x.first, x.second)
    nt = (a=1, b=2, c=3)
    map(f, nt |> pairs |> collect) # gives expected result: list of `f` applications to each pair
    map(f, nt |> pairs) # error: map is not defined on dictionaries
    dct = Dict(:a => 1, :b => 2, :c => 3)
    map(f, dct |> pairs |> collect) # gives expected result: list of `f` applications to each pair
    map(f, dct |> pairs) # error: map is not defined on dictionaries

```

Same with broadcasting instead of `map`. The error message is a bit less confusing in that case: `ArgumentError: broadcasting over dictionaries and `NamedTuple`s is reserved`, which is still not completely correct. Looks like mapping and broadcasting over dicts and namedtuples is disabled on purpose (sounds good, because there is no unambiguous way to iterate them), but somehow this also gets applied to `pairs` iterator which isn’t a dict nor a namedtuple.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [September 23, 2019, 7:32am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/8 "2019-09-23T07:32:53Z")

</div>

Yes, the error message is confusing. I thought there was an issue or even a PR about this, but I cannot dig it up at the moment.

---

<div class="post-metadata">

**Author:** ![aplavin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aplavin/32/222056_2.png) [@aplavin](https://discourse.julialang.org/u/aplavin)\
**Post date:** [September 23, 2019, 7:34am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/9 "2019-09-23T07:34:42Z")

</div>

But is the behavior intentional? As of now, it seems like there is no way to iterate over `pairs` without `collect`. Why disallow `map(f, pairs(nt))` in the first place?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [September 23, 2019, 7:43am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/10 "2019-09-23T07:43:02Z")

</div>

It is a consequence of `Pairs <: AbstractDict`.

Personally I consider this unfortunate, but I don’t know if there is any deeper reason for it. Like you said, leaving `map(f, ::NamedTuple)` and similar undefined is fine, but `pairs` should provide an iterable that just works in all contexts where iterables work.

That said, a current workaround is an “identity iterator” like

```julia
julia> n = (a = 5, b = 6)
(a = 5, b = 6)

julia> map(x -> (x..., ), Iterators.filter(_ -> true, pairs(n)))
2-element Array{Tuple{Symbol,Int64},1}:
 (:a, 5)
 (:b, 6)

```

(there are other options, didn’t benchmark).

---

<div class="post-metadata">

**Author:** ![zgornel](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zgornel/32/217487_2.png) [@zgornel](https://discourse.julialang.org/u/zgornel)\
**Post date:** [September 23, 2019, 8:07am UTC](https://discourse.julialang.org/t/map-over-namedtuple-keys-and-values-confusing-error/26866/11 "2019-09-23T08:07:45Z")

</div>

I do not believe there are any explicit conditions regarding `map`’s inputs however, looks to me that some implicit order (or index) of the elements needs to exist. `map` is not applicable on `AbstractSets` either which I believe is unfortunate as well. I would see no issue for example in `map(x->x+1, (i for i in 1:10))` to return `(x+1 for x in 1:10)` i.e. another iterator.
