Say I've got strings like "object.property.property" and I'd like to be able to use those strings to dynamically create form fields for those properties; I'd need to convert them to "object[property][property]".
I was trying something along the lines of: "object.property.property".split('.')
Which of course leaves me with an array: ["object", "property", "property"]
But I'm unsure how to rejoin them so the properties are surrounded by brackets ("object[property][property]"). I tried using join, but as far as I can tell that only lets you specify the seperator between each element in the array.
Any thoughts appreciated.
This is an option
result, *sub_str = "object.property.property".split('.')
result += sub_str.map{|property| "[#{property}]"}.join
#=> "object[property][property]"
"object.property.property".gsub('.', '][').sub('][', '[') << ']'
or, less productive, but more readable:
arr = "object.property.property".split('.')
[arr.shift, *arr.map { |e| "[#{e}]" }].join
Ah, the beauty of ruby:
obj, *props = "object.property.property".split('.')
obj #=> "object"
props #=> ["property", "property"]
"#{obj}[#{props.join('][')}]"
#=> "object[property][property]"
Update: to also cover objects without properties:
def form_name(property)
obj, *props = property.split('.')
"#{obj}[#{props.join('][')}]"
props.any? ? "#{obj}[#{props.join('][')}]" : obj
end
form_name "object.property.property"
#=> "object[property][property]"
form_name "object"
#=> "object"
I would suggest this:
"object.property.property".gsub(/\.([\w\d_]+[^\.])/, "[#{$1}]")
Though this is only readable to someone who understands Regular Expressions well.
Related
Example:
my_array = ['2823BII','4A','76B','10J']
[using magical method delete_if_doesnt_contain()]
my_array.map! do |elements|
elements.delete_if_doesnt_contain('A')
end
I want that to set my_array = ['4A']
Even if I could iterate through an array and just return the index of the element that contains an 'A' I'd be happy. Thanks for any help!
Thanks for the answers below, but one more question.
other_array = ['4']
my_var = other_array.to_s
my_array.select!{|x| x.include?(my_var)}
This isn't working for me. What am I missing? Something happen when I converted the array to a string?
Very easy using #select :
my_array = ['2823BII','4A','76B','10J']
my_array.select { |str| str.include?('A') }
# => ["4A"]
Or if you want to modify the source array, do use the bang version of select :-
my_array.select! { |str| str.include?('A') }
Arup's answer is correct.
However, to answer your last question specifically, "iterate through an array and just return the index of the element that contains an 'A'", the "index" method returns the index for a matching element:
my_array.index {|x| x.include?('A') }
The grep method is a lot shorter
my_array = ['2823BII','4A','76B','10J']
my_array.grep /A/
I'm newbie.
Help me, please.
I write Puppet function
Piece of code :
n_if={}
over_if = arguments[1]
over_if.each do |kk,vv|
weth={}
puts kk,vv,weth
weth = arguments[0]
weth['in_vlan'] = vv['in_vlan']
weth['options']['MTU'] = vv['mtu']
n_if['eth'+ kk.to_s]=weth
end
Data readed from 2 files, and passed into arguments[0] and arguments[1] respectively:
# template of ethernet interfaces
eth_:
method: "static"
family: "inet"
ip: ""
netmask: "255.255.0.0"
onboot: true
options:
MTU: ""
in_vlan: ""
# values for include into ethernet interfaces
eth_values:
0:
mtu: 1500
in_vlan: 15
1:
mtu: 9000
in_vlan: 125
I expect get hash with keys 'eth0' and 'eth1' as follow:
eth1methodstaticfamilyinetin_vlan125ipnetmask255.255.0.0onboottrueoptionsMTU9000eth0methodstaticfamilyinetin_vlan15ipnetmask255.255.0.0onboottrueoptionsMTU1500
But I get :
eth1methodstaticfamilyinetin_vlan125ipnetmask255.255.0.0onboottrueoptionsMTU9000eth0methodstaticfamilyinetin_vlan125ipnetmask255.255.0.0onboottrueoptionsMTU9000
What is my mistake?
First, some comments:
Your code is not indented in a way that most others do it, which makes it hard for others to help you. It should look something like this:
n_if={}
over_if = arguments[1]
over_if.each do |kk,vv|
weth={}
puts kk,vv,weth
weth = arguments[0]
weth['in_vlan'] = vv['in_vlan']
weth['options']['MTU'] = vv['mtu']
n_if['eth'+ kk.to_s]=weth
end
Perhaps your variable names make sense to you, but they don't make sense to me. What is n_if, weth, over_if, kk and vv?
You assign weth to be a hash inside your each, and then you assign it to be something else. What are you really trying to do?
You say that arguments[0] and arguments[1] are data read in from files. How are these read in? Are these YAML files? It would be helpful if you would include code to actually reproduce your problem. Pare it down to the essentials.
In Ruby it is generally more idiomatic and performant not to concatenate strings, but to use string interpolation:
n_if["eth#{kk}"] = weth
Now, some answers:
My guess is that your setup holds data like this:
arguments = {
"eth_"=>{
"method"=>"static",
"family"=>"inet",
"ip"=>"",
"netmask"=>"255.255.0.0",
"onboot"=>true,
"options"=>{"MTU"=>""},
"in_vlan"=>""
},
"eth_values"=>{
0=>{"mtu"=>1500, "in_vlan"=>15},
1=>{"mtu"=>9000, "in_vlan"=>125}
}
}
arguments[0] = arguments['eth_']
arguments[1] = arguments['eth_values']
I believe (based on many guesses as to what you have and what you may want) that your problem is this combination:
weth={}
weth=arguments[0]
I think your intent here is to say "weth is a hash type of object; now fill it with values from arguments[0]". What those lines actually say is:
Set weth to an empty hash.
Nevermind, throw away that empty hash and set weth to the same object as arguments[0].
Consequently, each time through the loop you are modifying the same hash with weth. Instead, I think you want to duplicate the hash for weth. Does the following modified code give you what you need?
n_if={}
over_if = arguments[1]
over_if.each do |kk,vv|
weth = arguments[0].dup
weth['in_vlan'] = vv['in_vlan']
weth['options']['MTU'] = vv['mtu']
n_if["eth#{kk}"]=weth
end
require 'pp' # for nice wrapping inspection
pp n_if
#=> {"eth0"=>
#=> {"method"=>"static",
#=> "family"=>"inet",
#=> "ip"=>"",
#=> "netmask"=>"255.255.0.0",
#=> "onboot"=>true,
#=> "options"=>{"MTU"=>9000},
#=> "in_vlan"=>15},
#=> "eth1"=>
#=> {"method"=>"static",
#=> "family"=>"inet",
#=> "ip"=>"",
#=> "netmask"=>"255.255.0.0",
#=> "onboot"=>true,
#=> "options"=>{"MTU"=>9000},
#=> "in_vlan"=>125}}
If not, please edit your question with more details on what you ACTUALLY have (hint: p arguments and show us the result) and what you really want as the result.
Edit: For fun, here's a functional transformation instead. It is left as an exercise to the reader to understand how it works and level-up their functional programming skills. Note that I have modified eth_values to match the hierarchy of the template so that simple merging can be applied. I've left the "MTU"=>"" and "in_vlan"=>"" entries in, but note that they are not necessary for the code to work, you could delete both (and the resulting "options"=>{}) and achieve the same result.
args = {
"eth_"=>{
"method"=>"static",
"family"=>"inet",
"ip"=>"",
"netmask"=>"255.255.0.0",
"onboot"=>true,
"options"=>{"MTU"=>""},
"in_vlan"=>""
},
"eth_values"=>{
0=>{"options"=>{"MTU"=>1500}, "in_vlan"=>15},
1=>{"options"=>{"MTU"=>9000}, "in_vlan"=>125}
}
}
n_if = Hash[
args['eth_values'].map do |num,values|
[ "eth#{num}",
args['eth_'].merge(values) do |k,v1,v2|
if v1.is_a?(Hash) and v2.is_a?(Hash) then
v1.merge(v2)
else
v2
end
end ]
end
]
pp n_if #=> Same result as in the previous code.
Rails fans are familiar with params[:terms] or a hash of 'things' passed to the controller collected form the url. E.g.:
params
=> {"term"=>"Warren Buffet",
"controller"=>"search",
"format"=>"json",
"action"=>"index"}
If I want to use "Warren Buffet", "Warren" and "Buffet" in the code below, does anyone know which method I should be using instead? gsub is close, but it takes each match and not the original string too. Unless I'm doing it wrong, which is totally possible:
#potential_investors = []
params[:term].gsub(/(\w{1,})/) do |term|
#potential_investors << User.where(:investor => true)
.order('first_name ASC, last_name ASC')
.search_potential_investors(term)
end
Thoughts?
How about:
s = "Filthy Rich"
s.split(" ").push(s)
>> ["Filthy", "Rich", "Filthy Rich"]
Or with scan if you prefer to use the regexp instead:
s.scan(/\w+/).push(s)
>> ["Filthy", "Rich", "Filthy Rich"]
params["term"].gsub(/(\w{1,})/)
returns an enumerator. You could convert it to an array and append the original term to it:
ary = params["term"].gsub(/(\w{1,})/).to_a + [params["term"]]
then process it:
ary.each do |term|
...
I have an each method that is run on some user-submitted data.
Sometimes it will be an array, other times it won't be.
Example submission:
<numbers>
<number>12345</number>
</numbers>
Another example:
<numbers>
<number>12345</number>
<number>09876</number>
</numbers>
I have been trying to do an each do on that, but when there is only one number I get a TypeError (Symbol as array index) error.
I recently asked a question that was tangentally similar. You can easily force any Ruby object into an array using Array.
p Array([1,2,3]) #-> [1,2,3]
p Array(123) #-> [123]
Of course, arrays respond to each. So if you force everying into an array, your problem should be solved.
A simple workaround is to just check if your object responds to :each; and if not, wrap it in an array.
irb(main):002:0> def foo x
irb(main):003:1> if x.respond_to? :each then x else [x] end
irb(main):005:1> end
=> nil
irb(main):007:0> (foo [1,2,3]).each { |x| puts x }
1
2
3
=> [1, 2, 3]
irb(main):008:0> (foo 5).each { |x| puts x }
5
=> [5]
It looks like the problem you want to solve is not the problem you are having.
TypeError (Symbol as array index)
That error tells me that you have an array, but are treating it like a hash and passing in a symbol key when it expects an integer index.
Also, most XML parsers provide child nodes as array, even if there is only one. So this shouldn't be necesary.
In the case of arguments to a method, you can test the object type. This allows you to pass in a single object or an array, and converts to an array only if its not one so you can treat it identically form that point on.
def foo(obj)
obj = [obj] unless obj.is_a?(Array)
do_something_with(obj)
end
Or something a bit cleaner but more cryptic
def foo(obj)
obj = [*obj]
do_something_with(obj)
end
This takes advantage of the splat operator to splat out an array if it is one. So it splats it out (or doesn't change it) and you can then wrap it an array and your good to go.
I was in the same position recently except the object I was working with was either a hash or an array of hashes. If you are using Rails, you can use Array.wrap because Array(hash) converts hashes to an array.
Array({foo: "bar"}) #=> [[:foo, "bar"]]
Array.wrap({foo: "bar"}) #=> [{:foo=>"bar"}]
Array.wrap(123) #=> [123]
Array.wrap([123]) #=> [123]
I sometimes use this cheap little trick:
[might_be_an_array].flatten.each { |x| .... }
Use the splat operator:
[*1] # => [1]
[*[1,2]] # => [1,2]
Like Mark said, you're looking for "respond_to?" Another option would be to use the conditional operator like this:
foo.respond_to? :each ? foo.each{|x| dostuff(x)} : dostuff(foo);
What are you trying to do with each number?
You should try to avoid using respond_to? message as it is not a very object oriented aproach.
Check if is it possible to find in the xml generator code where it is assigning an integer value when there is just one <"number"> tag and modify it to return an array.
Maybe it is a complex task, but I would try to do this in order to get a better OO design.
I don't know much anything about ruby, but I'd assume you could cast (explicitly) the input to an array - especially given that if the input is simply one element longer it's automatically converted to an array.
Have you tried casting it?
If your input is x, use x.to_a to convert your input into an array.
[1,2,3].to_a
=> [1, 2, 3]
1.to_a
=> [1]
"sample string".to_a
=> ["sample string"]
Edit: Newer versions of Ruby seem to not define a default .to_a for some standard objects anymore. You can always use the "explicit cast" syntax Array(x) to achieve the same effect.
I have an array like so:
["marblecake", "also", "the", 1337]
I would like to get back a string which contains each element of the array prefixed by some specified string, then joined together by another specified string. For example,
["marblecake", "also", "the", 1337].join_with_prefix("%", "__")
should result in
# => %marblecake__%also__%the__%1337
How might I do this?
If your array is in a then this one-liner will do it
a.map { |k| "%#{k}" }.join("_")
You could easily put this in a function of your own - even add it to the Array class so that you can call it on an array, like in your example.
Note that the '!' version of map (map!) will modify the array in place - perhaps not your intent.
Although this is very old question, I'd like to add this:
a.any? && "%".+(a.join("__%"))
As per the above suggestion:
class Array
def join_with_prefix(prefix,separator)
self.collect {|e| prefix.to_s + e }.join(separator)
end
end
p ['ab','cd','ef'].join_with_prefix('%','__')
=> "%ab__%cd__%ef"
I was looking for a solution to this today.I found this question.This was my solution.
array = ["marblecake", "also", "the", 1337];
arrayString = ''
array.each{|x| arrayString += "%" + x + "__"}