Condition Validation Yup- Any one field is required (Error: Uncaught Error: Cyclic dependency, the node was: "b".) - formik

In my application I'm using Yup validation. I faced a scenario where I need at least one of the three fields(String) required. I've tried using the below code but it is throwing Uncaught Error: Cyclic dependency, the node was: "b".
a: yup.string().when(['b', 'c'], {
is: (b, c) => !b && !c,
then: yup.string().required()
}),
b: yup.string().when(['a', 'c'], {
is: (a, c) => !a && !c,
then: yup.string().required()
}),
c: yup.string().when(['a', 'b'], {
is: (a, b) => !a && !b,
then: yup.string().required()
})
}, [['a', 'b'], ['a', 'c'], ['b','c']])```
Any response or working code would be very helpful. Thanks in advance.

I found that you can do this using the lazy construct in Yup.
Ref for lazy: https://github.com/jquense/yup#yuplazyvalue-any--schema-lazy
Creates a schema that is evaluated at validation/cast time. Useful for creating recursive schema like Trees, for polymorphic fields and arrays.
Example:
a: yup.lazy(() => yup.string().when(['b', 'c'], {
is: (b, c) => !b && !c,
then: yup.string().required()
})),
b: yup.lazy(() => yup.string().when(['a', 'c'], {
is: (a, c) => !a && !c,
then: yup.string().required()
})),
c: yup.lazy(() => yup.string().when(['a', 'b'], {
is: (a, b) => !a && !b,
then: yup.string().required()
}))
}, [['a', 'b'], ['a', 'c'], ['b','c']])```

Related

Compare 2 Hash with values consisting of array

I have 2 hashes, let's say A, B
A: { 'key1' => [a, b], 'key2' => 'c' }
B: { 'key1' => [b, a], 'key2' => 'c' }
What is the best possible way to compare these 2 hashes. The ordering of the array contents does not matter. So in my case, hash A and B are equal
It's not as easy as it seems at first glance.
It is necessary to take into account several nuances:
the number of elements in the hashes may not match;
items with the same key in two hashes can be of different types.
A relatively universal solution can be as follows:
def hashes_comp(hash1, hash2)
return false if hash1.size != hash2.size
hash1.each do |key, value|
if value.class == Array
return false if hash2[key].class != Array || value.sort != hash2[key].sort
else
return false if value != hash2[key]
end
end
true
end
hash_a = {'key1' => ['a', 'b'], 'key2' => 'c'}
hash_b = {'key1' => ['b', 'a'], 'key2' => 'c'}
hash_c = {'key1' => ['a', 'c'], 'key2' => 'c'}
hash_d = {'key1' => ['a', 'b'], 'key2' => 'd'}
hash_e = {'key1' => ['a', 'b'], 'key2' => ['a', 'b']}
hash_f = {'key1' => ['a', 'b'], 'key2' => 'c', 'key3' => 'd'}
hashes_comp(hash_a, hash_b) #=> true
hashes_comp(hash_a, hash_c) #=> false
hashes_comp(hash_a, hash_d) #=> false
hashes_comp(hash_a, hash_e) #=> false
hashes_comp(hash_a, hash_f) #=> false
One can sort the arrays but that can be an expensive operation if the arrays are large. If n equals the size of the array, the time complexity of heapsort, for example, is O(n log(n)). It's faster to replace arrays with counting hashes, the construction of which enjoys a time complexity of O(n).
h1 = { 'k1' => [1, 2, 1, 3, 2, 1], 'k2' => 'c' }
h2 = { 'k1' => [3, 2, 1, 2, 1, 1], 'k2' => 'c' }
def same?(h1, h2)
return false unless h1.size == h2.size
h1.all? do |k,v|
if h2.key?(k)
vo = h2[k]
if v.is_a?(Array)
if vo.is_a?(Array)
convert(v) == convert(vo)
end
else
v == vo
end
end
end
end
def convert(arr)
arr.each_with_object(Hash.new(0)) { |e,g| g[e] += 1 }
end
same?(h1, h2)
#=> true
Here
convert([1, 2, 1, 3, 2, 1])
#=> {1=>3, 2=>2, 3=>1}
convert([3, 2, 1, 2, 1, 1])
#=> {3=>1, 2=>2, 1=>3}
and
{1=>3, 2=>2, 3=>1} == {3=>1, 2=>2, 1=>3}
#=> true
See Hash::new, specifically the case where the method takes an argument that equals the default value.
The guard clause return false unless h1.size == h2.size is to ensure that h2 does not have keys that are not present in h1. Note that the following returns the falsy value nil:
if false
#...
end
#=> nil
In a couple of places I've written that rather than the more verbose but equivalent expresion
if false
#...
else
nil
end
I would definitely agree with Ivan it's not as easy as it initially seems but I figured I would try doing it with recursion. This has the added benefit of being able to compare beyond just hashes.
hash_a = {'key1' => ['a', 'b'], 'key2' => 'c'}
hash_b = {'key1' => ['b', 'a'], 'key2' => 'c'}
hash_c = {'key1' => ['a', 'c'], 'key2' => 'c'}
hash_d = {'key1' => ['a', 'b'], 'key2' => 'd'}
hash_e = {'key1' => ['a', 'b'], 'key2' => ['a', 'b']}
hash_f = {'key1' => ['a', 'b'], 'key2' => 'c', 'key3' => 'd'}
def recursive_compare(one, two)
unless one.class == two.class
return false
end
match = false
# If it's not an Array or Hash...
unless one.class == Hash || one.class == Array
return one == two
end
# If they're both Hashes...
if one.class == Hash
one.each_key do |k|
match = two.key? k
break unless match
end
two.each_key do |k|
match = one.key? k
break unless match
end
if match
one.each do |k, v|
match = recursive_compare(v, two[k])
break unless match
end
end
end
# If they're both Arrays...
if one.class == Array
one.each do |v|
match = two.include? v
break unless match
end
two.each do |v|
match = one.include? v
break unless match
end
end
match
end
puts recursive_compare(hash_a, hash_b) #=> true
puts recursive_compare(hash_a, hash_c) #=> false
puts recursive_compare(hash_a, hash_d) #=> false
puts recursive_compare(hash_a, hash_e) #=> false
puts recursive_compare(hash_a, hash_f) #=> false
I came up with this solution:
def are_equals?(a, b)
(a.keys.sort == b.keys.sort) &&
a.merge(b) { |k, o_val, n_val| [o_val, n_val].all? { |e| e.kind_of? Array} ? o_val.sort == n_val.sort : o_val == n_val }.values.all?
end
How it works.
The first part tests for key equality, using Hash#keys, which returns the array of keys, sorted of course:
a.keys.sort == b.keys.sort
For the second part I used Hash#merge to compare values related to the same key, and can be expanded in this way:
res = a.merge(b) do |k, o_val, n_val|
if [o_val, n_val].all? { |e| e.kind_of? Array}
o_val.sort == n_val.sort
else
o_val == n_val
end
end
#=> {"key1"=>true, "key2"=>true}
It returns a Hash where values are true or false, then checks if all values are true using Enumerable#all?:
res.values.all?
#=> [true, true].all? => true

Python: List all possible paths in graph represented by dictionary

I have a dictionary with keys representing nodes and values representing possible nodes that the key can traverse to.
Example:
dependecyDict = { 'A': ['D'], 'B': ['A', 'E'], 'C': ['B'], 'D': ['C'], 'G':['H']}
I want to create a new dicitonary, ChainsDict, that will contain all 'values' that each 'key' can traverse to by means of dependecyDict.
For example, the output of the program with this example will be:
ChainsDict = {'A': ['D', 'C', 'B','E'], 'B':['A','D','C','E'], 'C':['B','A','D','E'], 'D':['C','B','A','E'], 'G': ['H']}
I think using a recursive algorithm is the best way to go about making a solution and I tried modifying a shortest path traversing algorithm as follows:
def helper(dependencyDict, ChainsDict):path = []
for key in dependencyDict:
path = path + [(recursiveRowGen(dependencyDict,key))]
for paths in path:
ChainsDict[paths[0]] = paths[1:]
print(finalLineDict)
def recursiveRowGen(dependencyDict,key,path = []):
path = path + [key]
if not key in dependencyDict:
print("no key: ",key)
return path
print(dependencyDict[key])
for blocking in dependencyDict[key]:
if blocking not in path:
newpath = recursiveRowGen(dependencyDict,blocking,path)
if newpath:
return newpath
return path
This code however is having problems capturing the correct output when a key in dependecyDict has more than one value.
I found a hacky solution but it doesn't feel very elegant. Any help is appreciated, thanks!
Here is a recursive solution:
Code
def get_chain_d(argDict):
def each_path(i,caller_chain):
a=[]
caller_chain.append(i)
b = argDict.get(i,[])
for j in b:
if j not in caller_chain:
a.append(j)
a.extend(each_path(j,caller_chain))
return a
return {i:each_path(i,[]) for i in argDict}
dependecyDict = { 'A': ['D'], 'B': ['A', 'E'], 'C': ['B'], 'D': ['C'], 'G':['H']}
print(get_chain_d(dependecyDict))
Output:
{'B': ['A', 'D', 'C', 'E'], 'A': ['D', 'C', 'B', 'E'], 'D': ['C', 'B', 'A', 'E'], 'C': ['B', 'A', 'D', 'E'], 'G': ['H']}
This is basically a graph traversal problem. You can represent each of your key as a node in a graph and its values are the nodes it is connected to.
You can do either Depth-first-search or breadth-first-search for graph. Of course, there's also an iterative and a recursive solution for each of these method. Here's an iterative implementation (I added a few conditionals to eliminate loops):
dependencyDict = { 'A': ['D'], 'B': ['A', 'E'], 'C': ['B'], 'D': ['C'], 'G':['H'] }
chainsDict = {}
for key in dependencyDict:
currKey = key
frontier = [key]
visited = []
while frontier:
currKey = frontier[0]
frontier.remove(currKey)
if dependencyDict.get(currKey,0) and (currKey not in visited) and (currKey not in frontier):
nodes = dependencyDict[currKey]
frontier.extend(nodes)
visited.append(currKey)
elif currKey in visited:
visited.remove(currKey)
elif dependencyDict.get(currKey,0) == 0:
visited.append(currKey)
for i in visited:
if i == key:
visited.remove(i)
chainsDict[key] = visited
print chainsDict
The result looks like:
{'A': ['D', 'C', 'B', 'E'], 'C': ['B', 'A', 'E', 'D'], 'B': ['A', 'E', 'D', 'C'], 'D': ['C', 'B', 'A', 'E'], 'G': ['H']}

Multi-parameter case

I want to create a case checking multiple parameters.
"Ruby: conditional matrix? case with multiple conditions?"
is basically it with one twist: The second parameter can be one of three values, a, b, nil.
I was hoping to just extend the when conditions to be something like:
result = case [A, B]
when [true, ‘a’] then …
when [true, ‘b’] then …
when [true, B.nil?] then …
end
Any suggestions?
In the comments is already the answer for your specific test for nil:
result = case [A, B]
when [true, 'a'] then …
when [true, 'b'] then …
when [true, nil] then …
end
But your question inspired me for a more extended question: What if the 2nd parameter could be anything? E.g. you have this decision table:
A B result
------------------
a b true
a _ halftrue
_ b halftrue
else false
where _ is a indicator for anything
A possible solution would be a class, that is equal to everything:
class Anything
include Comparable
def <=>(x);0;end
end
[
%w{a b},
%w{a x},
%w{x b},
%w{x y},
].each{|a,b|
result = case [a, b]
when ['a', 'b'] then true
when ['a', Anything.new] then :halftrue
when [Anything.new, 'b'] then :halftrue
else false
end
puts "%s %s: %s" % [a,b,result]
}
The result:
a b: true
a x: halftrue
x b: halftrue
x y: false

Multiple default parameters in function do not work [duplicate]

This question already has answers here:
Invalid function in ruby
(3 answers)
Closed 7 years ago.
I am trying to understand why the below function gives compilation error:
# Works fine
def test(a, b=10, c=20)
p a, b, c
end
# Works fine
def test(a=10, b=20, c)
p a, b, c
end
# Gives error - syntax error, unexpected '=', expecting ')'
def test(a=10, b, c=20)
p a, b, c
end
It seems multiple default parameters do not work if they are all not either at beginning or end of parameter list.
Ruby allows mandatory and optional parameters to be defined in a flexible manner. These all work and are logical:
def foo(a); end
def foo(a, b); end
def foo(a, b=1); end
def foo(a=1, b); end
def foo(a=1, b=1); end
If you need three or four parameters, start considering whether you should switch to a different way of passing in the parameters. It's a maintenance and readability thing.
Instead of anything that looks like
def test(param1=nil, param2, param3, param4, param5, param6, param7, param8, param9, param10)
WAY before that point, switch to something more concise. Any of these would be preferable:
def foo(opts)
a, b, c = opts.values_at(*%i[a b c]).map{ |v| v.nil? ? 'nil' : v }
"a: '#{a}' b: '#{b}' c: '#{c}'"
end
foo(a:'a') # => "a: 'a' b: 'nil' c: 'nil'"
foo(a:'a', b:'b') # => "a: 'a' b: 'b' c: 'nil'"
foo(b:'b') # => "a: 'nil' b: 'b' c: 'nil'"
foo(a:'a', b:'b', c: 'c') # => "a: 'a' b: 'b' c: 'c'"
or:
Options = Struct.new('Options', 'a', 'b', 'c')
def foo(opts)
a, b, c = opts.values.map{ |v| v.nil? ? 'nil' : v }
"a: '#{a}' b: '#{b}' c: '#{c}'"
end
foo(Options.new('a', nil, 'c')) # => "a: 'a' b: 'nil' c: 'c'"
foo(Options.new('a', 'b', 'c')) # => "a: 'a' b: 'b' c: 'c'"
foo(Options.new(nil, 'b', 'c')) # => "a: 'nil' b: 'b' c: 'c'"
or:
require 'ostruct'
def foo(opts)
a, b, c = opts.to_h.values_at(*%i[a b c]).map{ |v| v.nil? ? 'nil' : v }
"a: '#{a}' b: '#{b}' c: '#{c}'"
end
foo(OpenStruct.new(:a => 'a')) # => "a: 'a' b: 'nil' c: 'nil'"
foo(OpenStruct.new(:a => 'a', :b => 'b')) # => "a: 'a' b: 'b' c: 'nil'"
foo(OpenStruct.new(:a => 'a', :b => 'b', :c => 'c')) # => "a: 'a' b: 'b' c: 'c'"
Using that sort of parameter passing greatly reduces the noise. Inside the method you can look for values that aren't initialized and force their default values and/or raise an error if you didn't receive a mandatory value.
Paraphrasing the rules of parameter binding from this answer related to default parameters.
As long as there are unbound mandatory parameters at the beginning of the parameter list, bind arguments left-to-right
As long as there are unbound mandatory parameters at the end of the parameter list, bind arguments right-to-left
Any leftover arguments are bound to optional parameters left-to-right
Any leftover arguments are collected into an array and bound to the splat argument
A block is wrapped up into a Proc and bound to the block argument
If there are any unbound parameters or leftover arguments, raise an ArgumentError
If we read the above rules carefully, it seems to imply that:
If you don't have mandatory parameter(s) either at the beginning or end of parameter list, then, all parameters should be optional.
Ruby seems to be enforcing the above rules in the below function definition:
# Gives error - syntax error, unexpected '=', expecting ')'
def test(a=10, b, c=10)
p a, b, c
end
If one really needs optional parameters at beginning and end, one can use something like below - it uses default parameters at beginning and keyword parameters at end.
def test(a=10, b=20, c, d: 40, e: 40)
p a, b, c, d, e
end
test (30)
#=> 10, 20, 30, 40, 50
However, below will not work:
def test(a=10, b=20, c, d: 40, e: 40, f)
p a, b, c, d, e
end
# Will produce below error
# syntax error, unexpected tIDENTIFIER
# def test(a=10, b=20, c, d: 40, e: 40, f)
^

How to get specific value within an array of arrays

I have an array (outside array) that contains three arrays (inside arrays), each of which have three elements.
array = [[a, b, c], [d, e, f], [g, h, i]]
I want to select the specific inside array using an index of the outside array and then select the value within the selected inside array based off its index. Here is what I tried:
array.each_index{|i| puts "letter: #{array[i[3]]} " }
I was hoping that would give me the following output
letter: c letter: f letter: i
but instead, I get
letter: [[a, b, c], [d, e, f], [g, h, i]]
I also tried
array.each_index{|i| puts "letter: #{array[i][3]} " }
but I get the same result. Please any suggestions are very appreciated. I need a simple explanation.
each_index is an Enumerable which goes through all indices and performs an action on each one. When it's done it will return your original collection as it's not its job to change it. If you want to output stuff on the screen via puts / print then each_index is fine.
If you want to create a new collection as a result of going through all the elements of an original collection, you should use map.
e.g.
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
new_array = array.map {|el| el[2]}
=> ["c", "f", "i"]
array.map iterates through array's elements so in every step |el| is an element, not an index, as in: ['a', 'b', 'c'] in the first iteration, ['d', 'e', 'f'] in the second one and so on...
Just pointing this out since I don't know what's the goal of what you're trying to do.
do it like this:
array.each_index{|i| puts "letter: #{array[i][2]} " }
Since you want letter at index 2, not 3.
Also array should be defined like this:
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
You could use map like so:
a = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
a.map(&:last)
# => ["c", "f", "i"]
Or if you really want the puts and not the collected values:
a.each {|v| puts "letter: #{v.last}"}
You could also use Ruby's Matrix class if there's more Matrix-y stuff you want to do:
require 'matrix'
m = Matrix[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
last_column = m.column_vectors.last
# => Vector["c", "f", "i"]
You can now use any of the Vector methods on last_column, including to_a, which will put you back in familiar territory with the last column's values.
Arrays in ruby are indexed from 0, not from 1. So:
array.each_index{|i| puts "letter: #{array[i][2]} " }
should give you what you want.
You could try the below also:
p RUBY_VERSION
arr = [[1,2,3],[4,5,6],[11,12,13]]
arr.each{|x| p x; x.each_index{|i| p "Digit :: #{x[i]}" if i == 2} }
output:
"2.0.0"
[1, 2, 3]
"Digit :: 3"
[4, 5, 6]
"Digit :: 6"
[11, 12, 13]
"Digit :: 13

Resources