I wonder if it is possible to concatenate a variable value or a string to a new variable value declaration in Ruby.
foo = "something"
#new variable declaration:
var_ + foo = "concat variable name"
p var_foo # => "concat variable name"
n = 2
position + n = Array.new(3, 1)
p position2 # => [1, 1, 1]`
Thank you very much
In such a scenario it's probably better to use a Hash instead.
values = {}
values['foo'] = 'something'
values['var_' + 'foo'] = 'concat variable name'
p values['var_foo'] #=> "concat variable name"
n = 2
values["position#{n}"] = Array.new(3, 1)
p values['position2'] #=> [1, 1, 1]
Related
String:
string = "this;is;a;string;yes"
I can split the string and append each element to an array like this
arr = []
string.split(";").each do |x|
arr << x
end
Is there an easy way to take the first third and fourth values other than something like this.
x = 0
string.split(";").each do |x|
if x == 0 or x == 2 or x == 3 then arr << x end
x += 1
end
Sure. Use Array#values_at:
string = "this;is;a;string;yes"
string.split(";").values_at(0, 2, 3)
# => ["this", "a", "string"]
See it on repl.it: https://repl.it/#jrunning/FussyRecursiveSpools
I want to change the value of an array via a hash, for example:
arr = ['g','g','e','z']
positions = {1 => arr[0], 2 => arr[1]}
positions[1] = "ee"
Problem is that the one that changed is hash and not array. When I do p arr It still outputs ['g','g','e','z']. Is there a way around this?
You're going to need to add another line of code to do what you want:
arr = ['g','g','e','z']
positions = {1 => arr[0], 2 => arr[1]}
positions[1] = "ee"
arr[0] = positions[1]
Another option would be to make a method that automatically updated the array for you, something like this:
def update_hash_and_array(hash, array, val, index)
# Assume that index is not zero indexed like you have
hash[index] = val
array[index - 1] = val
end
update_hash_and_array(positions, arr, "ee", 1) # Does what you want
This is possible to code into your hash using procs.
arr = ['g','g','e','z']
positions = {1 => -> (val) { arr[0] = val } }
positions[1].('hello')
# arr => ['hello', 'g', 'e', 'z']
You can generalize this a bit if you want to generate a hash that can modify any array.
def remap_arr(arr, idx)
(idx...arr.length+idx).zip(arr.map.with_index{|_,i| -> (val) {arr[i] = val}}).to_h
end
arr = [1,2,3,4,5,6]
positions = remap_arr(arr, 1)
positions[2].('hello')
# arr => [1,'hello',3,4,5,6]
positions[6].('goodbye')
# arr => [1,'hello',3,4,5,'goodbye']
But I'm hoping this is just a thought experiment, there is no reason to change the way array indexing behavior works to start from 1 rather than 0. In such cases, you would normally just want to offset the index you have to match the proper array indexing (starting at zero). If that is not sufficient, it's a sign you need a different data structure.
#!/usr/bin/env ruby
a = %w(q w e)
h = {
1 => a[0]
}
puts a[0].object_id # 70114787518660
puts h[1].object_id # 70114787518660
puts a[0] === h[1] # true
# It is a NEW object of a string. Look at their object_ids.
# That why you can not change value in an array via a hash.
h[1] = 'Z'
puts a[0].object_id # 70114787518660
puts h[1].object_id # 70114574058580
puts a[0] === h[1] # false
h[2] = a
puts a.object_id # 70308472111520
puts h[2].object_id # 70308472111520
puts h[2] === a # true
puts a[0] === h[2][0] # true
# Here we can change value in the array via the hash.
# Why?
# Because 'h[2]' and 'a' are associated with the same object '%w(q w e)'.
# We will change the VALUE without creating a new object.
h[2][0] = 'X'
puts a[0] # X
puts h[2][0] # X
puts a[0] === h[2][0] # true
The array counts is as follows:
counts = ["a", 1]
What does this:
counts[0][0]
refer to?
I've only seen this before:
array[idx]
but never this:
array[idx][idx]
where idx is an integer.
This is the entire code where the snippet of code before was from:
def num_repeats(string) #abab
counts = [] #array
str_idx = 0
while str_idx < string.length #1 < 4
letter = string[str_idx] #b
counts_idx = 0
while counts_idx < counts.length #0 < 1
if counts[counts_idx][0] == letter #if counts[0][0] == b
counts[counts_idx][1] += 1
break
end
counts_idx += 1
end
if counts_idx == counts.length #0 = 0
# didn't find this letter in the counts array; count it for the
# first time
counts.push([letter, 1]) #counts = ["a", 1]
end
str_idx += 1
end
num_repeats = 0
counts_idx = 0
while counts_idx < counts.length
if counts[counts_idx][1] > 1
num_repeats += 1
end
counts_idx += 1
end
return counts
end
The statement
arr[0]
Gets the first item of the array arr, in some cases this may also be an array (Or another indexable object) this means you can get that object and get an object from that array:
# if arr = [["item", "another"], "last"]
item = arr[0]
inner_item = item[0]
puts inner_item # => "item"
This can be shortened to
arr[0][0]
So any 2 dimensional array or array containing indexable objects can work like this, e.g. with an array of strings:
arr = ["String 1", "Geoff", "things"]
arr[0] # => "String 1"
arr[0][0] # => "S"
arr[1][0] # => "G"
It's for nested indexing
a = [ "item 0", [1, 2, 3] ]
a[0] #=> "item 0"
a[1] #=> [1, 2, 3]
a[1][0] #=> 1
Since the value at index 1 is another array you can use index referencing on that value as well.
EDIT
Sorry I didn't thoroughly read the original question. The array in question is
counts = ["a", 1]
In this case counts[0] returns "a" and since we can use indexes to references characters of a string, the 0th index in the string "a" is simply "a".
str = "hello"
str[2] #=> "l"
str[1] #=> "e"
I am curious about a feature of the .each method.
a = 1
b = 2
[a,b].each do |x|
puts x
end
Is there a way for ruby to return the variable "a" rather than the value 1?
It doesn't return 1, it returns [1, 2], the each method returns what it iterated over.
> a = 1
=> 1
> b = 2
=> 2
> r = [a, b].each { |x| puts x }
1
2
=> [1, 2]
> p r.inspect
"[1, 2]"
If you're asking if you can "go backwards" from the array value, or the variable inside the iteration block, I don't see how. If you were iterating over a map with key/value pairs, yes.
> m = { a: 1, b: 2}
=> {:a=>1, :b=>2}
> m.each { |k, v| p "#{k} = #{v}" }
"a = 1"
"b = 2"
I know of the ||= operator, but don't think it'll help me here...trying to create an array that counts the number of "types" among an array of objects.
array.each do |c|
newarray[c.type] = newarray[c.type] ? newarray[c.type]+1 ? 0
end
Is there a more graceful way to do this?
types = Hash.new(-1) # It feels like this should be 0, but to be
# equivalent to your example it needs to be -1
array.each do |c|
types[c.type] += 1
end
Use the Array#fetch method for which you can provide a default value if the index doesn't exist:
array.each do |c|
newarray[c.type] = newarray.fetch(c.type, -1) + 1
end
array.each do |c|
newarray[c.type] = 1 + (newarray[c.type] || -1)
end
Alternatively
array.each do |c|
newarray[c.type] ||= -1
newarray[c.type] += 1
end
||= does help:
types = {}
array.each do |c|
types[c.class] ||= 0
types[c.class] += 1
end
Your variable newarray is named oddly, since in Ruby and most other languages, arrays are indexed by integers, not random Objects like Class. It's more likely this is a Hash.
Also, you should be using c.class, instead of c.type, which is deprecated.
Finally, since you're creating a Hash, you can use inject like so:
newarray = array.inject( {} ) do |h,c|
h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0
h
end
Or, for a one-liner:
newarray = array.inject( {} ) { |h,c| h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 ; h }
As you can see, this gives the desired results:
irb(main):001:0> array = [1, {}, 42, [], Object.new(), [1, 2, 3]]
=> [1, {}, 42, [], #<Object:0x287030>, [1, 2, 3]]
irb(main):002:0> newarray = array.inject( {} ) { |h,c| h[c.class] = h.key?(c.class) ? h[c.class]+1 : 0 ; h }
=> {Object=>0, Hash=>0, Array=>1, Fixnum=>1}
In Ruby 1.8.7 or later you can use group_by and then turn each list of elements into count - 1, and make a hash from the array returned by map.
Hash[array.group_by(&:class).map { |k,v| [k, v.size-1] }]