# Dry-validation (v1.0.0rc3) - dynamic keys

**URL:** <https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782>\
**Category:** dry-validation\
**Created:** [May 18, 2019, 10:31am UTC](https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782 "2019-05-18T10:31:57Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![ndan](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/ndan/32/412_2.png) [@ndan](https://discourse.dry-rb.org/u/ndan)\
**Post date:** [May 18, 2019, 10:31am UTC](https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782/1 "2019-05-18T10:31:57Z")

</div>

I’m moving validation from ActiveRecord to dry-validation and I need to validate nested hash with dynamic keys:

```ruby
{ 
  ...
  "users_attributes" => { 
    "0" => { "name" => "John Doe" }, 
    "1" => { "name" => "Dan Brown" },
    ...
  } 
}

```

I don’t care about keys (eg “0”, “1”), but I’d like to validate each value (eg `required(:name).filled(:string)`)

Is there any way to do so with dry-schema (\>= v1.0.2) and/or dry-validation (\>= v1.0.0.rc3)?

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [May 20, 2019, 6:08am UTC](https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782/2 "2019-05-20T06:08:17Z")

</div>

This is currently not supported by the schema. I’ll add support for dynamic keys in `1.x` but for now you could work around this limitation by using contract rules:

```ruby
class MyContract < Dry::Validation::Contract
  UserAttribute = Dry::Schema.Params do
    required(:name).filled(:string)
  end

  params do
    required(:users_attributes).filled(:hash)
  end

  rule(:users_attributes) do
    values[:users_attributes].each do |key, value|
      res = UserAttribute.(value)

      next if res.success?

      key([:users_attributes, key]).failure(res.to_h)
    end
  end
end

```

---

<div class="post-metadata">

**Author:** ![ndan](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/ndan/32/412_2.png) [@ndan](https://discourse.dry-rb.org/u/ndan)\
**Post date:** [May 20, 2019, 8:04am UTC](https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782/3 "2019-05-20T08:04:56Z")

</div>

Thanks, unfortunately `key([:users_attributes, key]).failure(res.to_h)` doesn’t work.  
The only solution I found:

```ruby
res.errors.to_h.each do |name, messages|
  messages.each do |msg|
    key([key_name, name]).failure(msg)
  end
end

```

The downside of `rule(:user_attributes)` that it won’t remove unknown keys

```ruby
attrs = {
  "users_attributes" => {
    "0" => { "wrong_key" => 0, "name" => "John Doe" },
  }
}

MyContract.new.(attrs).to_h
=> {:users_attributes=>{"0"=>{"unknown_key"=>0, "name"=>"John Doe"}}}

```

Is there any way to remove `unknown_key` from the result, so I can get rid of `strong_parameters`?

---

<div class="post-metadata">

**Author:** ![solnic](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/solnic/32/779_2.png) [@solnic](https://discourse.dry-rb.org/u/solnic)\
**Post date:** [May 20, 2019, 8:54am UTC](https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782/4 "2019-05-20T08:54:30Z")

</div>

Ah right, key processing won’t be triggered at all in such a case. I’m afraid this really has to wait until we have proper support for dynamic keys ☹

---

<div class="post-metadata">

**Author:** ![ndan](https://yyz1.discourse-cdn.com/flex031/user_avatar/discourse.dry-rb.org/ndan/32/412_2.png) [@ndan](https://discourse.dry-rb.org/u/ndan)\
**Post date:** [May 20, 2019, 9:08am UTC](https://discourse.dry-rb.org/t/dry-validation-v1-0-0rc3-dynamic-keys/782/5 "2019-05-20T09:08:41Z")

</div>

Got it,

It seems the best solution, for now, is to convert hashes into array

```ruby
class MyContract < Dry::Validation::Contract
  params do
    required(:users_attributes).array(:hash) do
      required(:name).filled(:string)
    end
  end
end

attrs = {
  "users_attributes" => {
    "0" => { "unknown_key" => 0, "name" => "John Doe" },
    "1" => { "a" => "Dan Brown" },
  }.values
}

result = MyContract.new.(attrs)
result.to_h # => {:users_attributes=>[{:name=>"John Doe"}, {}]}
result.errors.to_h # => {:users_attributes=>{1=>{:name=>["is missing"]}}}

```
