 
   defp custom_validator(_, _), do: :ok
 
-  # Returns a map containing only keys that `map_1` and `map_2` have in common.
-  # Values for the returned map are taken from `map_2`.
-  @spec intersection(map, map) :: map
-  defp intersection(map_1, map_2) when is_map(map_1) and is_map(map_2),
-    do:
-      for(
-        key <- Map.keys(map_1),
-        true == Map.has_key?(map_2, key),
-        into: %{},
-        do: {key, Map.get(map_2, key)}
-      )
+  defp delete_properties(map, %{properties: nil}), do: map
+
+  defp delete_properties(map, %{properties: properties}) do
+    map
+    |> Enum.filter(fn {key, _value} -> !Map.has_key?(properties, key) end)
+    |> Enum.into(%{})
+  end
+
+  defp delete_patterns(map, %{pattern_properties: nil}), do: map
+
+  defp delete_patterns(map, %{pattern_properties: patterns}) do
+    patterns = Map.keys(patterns)
+
+    map
+    |> Enum.filter(fn {key, _value} -> !match_any?(patterns, key) end)
+    |> Enum.into(%{})
+  end
+
+  defp match_any?(patterns, pattern) when is_atom(pattern) do
+    match_any?(patterns, Atom.to_string(pattern))
+  end
+
+  defp match_any?(patterns, pattern) when is_binary(pattern) do
+    Enum.any?(patterns, fn regex -> Regex.match?(regex, pattern) end)
+  end
+
+  defp collect(results) do
+    Enum.reduce(results, :ok, fn
+      :ok, :ok ->
+        :ok
+
+      {:error, reason}, :ok ->
+        {:error, [reason]}
+
+      :ok, {:error, _} = error ->
+        error
+
+      {:error, reason}, {:error, reasons} ->
+        {:error, [reason | reasons]}
+    end)
+  end
 end

    end
  end

  @spec properties(Schema.t(), map, keyword) :: {:ok, map} | {:error, map}
  defp properties(%{properties: nil}, map, _opts), do: {:ok, map}

  defp properties(%{properties: props}, map, opts),
    do: do_properties(Map.to_list(props), map, %{}, opts)

  @spec do_properties(list, map, map, keyword) :: result
  defp do_properties([], map, errors, _opts) when errors == %{}, do: {:ok, map}

  defp do_properties([], _map, errors, _opts),
    do: {:error, %{properties: errors}}

  defp do_properties([{prop, schema} | props], map, errors, opts) do
    with {:ok, value} <- Map.fetch(map, prop),
         :ok <- do_validate(schema, value, opts) do
      # The list `props` can contain multiple schemas for the same value.
      # The value will be just deleted if one schema is available.
      # Multiple schemas are coming from `pattern_properties`.
      map =
        case has_key?(props, prop) do
          true -> map
          false -> Map.delete(map, prop)
        end

      do_properties(props, map, errors, opts)
    else
      # The property is not in the map.

    end
  end

  @spec size(Schema.t(), map | keyword) :: result
  defp size(%{min_properties: nil, max_properties: nil}, _value), do: :ok

  defp size(%{min_properties: min, max_properties: max}, value) when is_map(value) do
    do_size(length(Map.keys(value)), min, max, value)
  end

  defp size(%{min_properties: min, max_properties: max}, value) when is_list(value) do
    do_size(length(value), min, max, value)
  end

  @spec do_size(number, number, number, map | keyword) :: result
  defp do_size(len, min, _max, value) when not is_nil(min) and len < min do
    {:error, %{min_properties: min, value: value}}
  end

  defp do_size(len, _min, max, value) when not is_nil(max) and len > max do
    {:error, %{max_properties: max, value: value}}
  end

  defp do_size(_len, _min, _max, _map), do: :ok

  @spec patterns(Schema.t(), map, keyword) :: :ok | {:error, map}
  defp patterns(%{pattern_properties: nil}, _map, _opts), do: :ok

  defp patterns(%{pattern_properties: patterns}, map, opts) do
    props =
