Traversing a single or an array of objects
I will explain a simple pattern to process a single or an array of objects uniformly. Since ruby is not a strongly typed language(< ruby 3), the burden may lie on the input function.
I will start with an example.
Let say we need to parse an XML schema. Which may or may not have a repeating block.
Example 1
<?xml version="1.0" encoding="UTF-8"?>
<comments>
<comment>
<description>TEST COMMENT 123</description>
</comment>
</comments>
Example 2
<?xml version="1.0" encoding="UTF-8"?>
<comments>
<comment>
<description>TEST COMMENT 123</description>
</comment>
<comment>
<description>TEST COMMENT 456</description>
</comment>
</comments>
First lets write a function to parse the data. I will be using nori
require 'nori'
def fetch_descriptions_1(raw_data:)
comments = Nori.new.parse(raw_data)['comments']['comment']
end
Next, let us try to fetch the descriptions. Since comment can be both a single object
or an array of objects
. We can use a simple if-else clause for this.
def fetch_descriptions_1(raw_data:)
comments = Nori.new.parse(raw_data)['comments']['comment']
if comments.is_a?(Array)
comments.map { |comment| comment['description'] }
else
[comments['description']]
end
end
The above perfectly handles the volatile nature of the input data.
But we can do this in a better manner. There is an opportunity to refactor.
Consider this, if we put the single_comment inside an array, then we will only need to handle a single input type, array of objects
.
def fetch_descriptions_2(raw_data:)
comments = [Nori.new.parse(raw_data)['comments']['comment']].flatten
comments.map { |comment| comment['description'] }
end
Full example
require 'nori'
single_comment = <<~EOF
<?xml version="1.0" encoding="UTF-8"?>
<comments>
<comment>
<description>TEST COMMENT 123</description>
</comment>
</comments>
EOF
multiple_comments = <<~EOF
<?xml version="1.0" encoding="UTF-8"?>
<comments>
<comment>
<description>TEST COMMENT 123</description>
</comment>
<comment>
<description>TEST COMMENT 456</description>
</comment>
</comments>
EOF
def fetch_descriptions_1(raw_data:)
comments = Nori.new.parse(raw_data)['comments']['comment']
if comments.is_a?(Array)
comments.map { |comment| comment['description'] }
else
[comments['description']]
end
end
def fetch_descriptions_2(raw_data:)
comments = [Nori.new.parse(raw_data)['comments']['comment']].flatten
comments.map { |comment| comment['description'] }
end
Comments