I have this nested array:
array = [
["A", "X"],
["B", "Y"],
["C", "Z"]
]
Is there a function that returns "B" when I provide "Y" and "C" when I provide "Z"?
rassoc might be what you need.
array.rassoc("Y") would return ["B", "Y"] and you can use first to get only the "B".
You can use find method.
array = [
["A", "X"],
["B", "Y"],
["C", "Z"]
]
str = "Y"
arr = array.find{|a| a[1] == str}
puts arr[0] if arr
# => B
Or, you can convert it to a Hash, if you need to do many lookups and the array is biggish:
hash = array.map(&:reverse).to_h
hash["Y"]
# => "B"
There is no such internal function out of the box, but one might easily create one:
▶ λ = ->(input) { array.detect { |e| e.last == input }.first rescue nil }
#⇒ #<Proc:0x0000000437f150#(pry):10 (lambda)>
▶ λ.('X')
#⇒ "A"
▶ λ.('Y')
#⇒ "B"
▶ λ.('QQQ')
#⇒ nil
Related
This question already has answers here:
Why is this array out of index?
(2 answers)
Closed 4 years ago.
I want to append elements from one 2D array to the another 2D array, but I get fatal error index out of bound.
the code is as follows:
var array = [["a", "b", "c"], ["d","e","f"],["g","h","i"]]
var array2 = [[String]]()
var x = array.count
var y = array[1].count
for j in 0..<x {
for i in 0..<y {
array2[j].append(array[j][i])
}
}
print(array2)
please don't tell me to just copy the array as this is not what I need, I am using this procedure to do something more complex than just copying an array.
Any suggestions as to why it goes out of bounds?
thanks
array2[j] doesn't exist as it's just an empty array.
It would be much easier to do this:
var array = [["a", "b", "c"], ["d","e","f"],["g","h","i"]]
var array2 = [[String]]()
for item in array {
array2.append(item)
}
print(array2)
[["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]
But that is just copying the array exactly. You should provide a more precise example of what you are trying to achieve.
Another option (Which makes your current code work) is to create a 'row' in the first loop ready for insertion:
var array = [["a", "b", "c"], ["d","e","f"],["g","h","i"]]
var array2 = [[String]]()
var x = array.count
var y = array[1].count
for j in 0..<x {
array2.append([String]())
for i in 0..<y {
array2[j].append(array[j][i])
}
}
print(array2)
Which gives the same output:
[["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"]]
It is going out of bounds because array2[0] doesn't exist. You need to create an an empty array there before appending to it. Then append that array to the outer array.
var array = [["a", "b", "c"], ["d","e","f"],["g","h","i"]]
var array2 = [[String]]()
for j in 0..<array.count {
var stringArray: [String] = []
for i in 0..<array[j].count {
stringArray.append(array[j][i])
}
array2.append(stringArray)
}
print(array2)
I have this special array var myArray : [Array<String>] = [["a"],["b"],["c"]]
I want to detect if "a" is already inside myArray
and after i would like to sort my array alphabetically but i havn't found any function to do all these things for my array
To find if your [[String]] contains "a", you can use contains twice:
var myArray : [Array<String>] = [["a"],["b"],["c"]]
if myArray.contains(where: { $0.contains("a") }) {
print("a found")
}
To sort the inner arrays, apply map to the outer array and sort each element:
var myArray : [Array<String>] = [["c", "a"], ["e", "b"], ["d"]]
let sorted = myArray.map { $0.sorted() }
print(sorted)
[["a", "c"], ["b", "e"], ["d"]]
Ruby 2.4. I want to create a new array by removing an element at a specified index from an array. I thought delete_at was the way, but it performs in-place and doesn't return the newly created array, but rather, the element that was removed:
2.4.0 :003 > a = ["a", "b", "c"]
=> ["a", "b", "c"]
2.4.0 :004 > a.delete_at(0)
=> "a"
2.4.0 :005 > a
=> ["b", "c"]
How do I delete an element from an array at a specified index but not perform the operation in place?
You can duplicate array and remove element from this duplicate. Use tap to return array, but not a deleted element.
2.3.3 :018 > a = ["a", "b", "c"]
=> ["a", "b", "c"]
2.3.3 :019 > b = a.dup.tap{|i| i.delete_at(0)}
=> ["b", "c"]
2.3.3 :020 > b
=> ["b", "c"]
Another way is to use reject with with_index:
2.3.3 :042 > b = a.reject.with_index{|v, i| i == 0 }
=> ["b", "c"]
2.3.3 :043 > b
=> ["b", "c"]
You wish to create a new array which is the same as a given array less the element at a given index.
You could use Array#[] (aka Array#slice) and Array#concat.
def copy_wo_element(arr, index_to_exclude)
arr[0,index_to_exclude].concat(arr[index_to_exclude+1..-1])
end
arr = [1,2,3,4,5]
copy_wo_element(arr, 0)
#=> [2, 3, 4, 5]
copy_wo_element(arr, 1)
#=> [1, 3, 4, 5]
copy_wo_element(arr, 2)
#=> [1, 2, 4, 5]
copy_wo_element(arr, 3)
#=> [1, 2, 3, 5]
copy_wo_element(arr, 4)
#=> [1, 2, 3, 4]
You could instead write
arr[0,index_to_exclude] + arr[index_to_exclude+1..-1]
but the use of concat avoids the creation of the temporary array arr[index_to_exclude+1..-1].
I'm using Ruby 2.4. I know how to find all the indexes in an array of elements matching a condition ...
arr.each_index.select{|i| arr[i] == 'x'}
but how do I find the index of the first element that matches a condition starting from a particular position in teh array? So what if I wanted to find a string with only a single character at or after index = 2? (If tehre are less than 2 elements the operation can return nil). So for example, if I have
["abc", "d", "efg", "h", "abcde"]
the operation would return "3", since element "h" is at position 3, only has a single character and is at or after index 2.
Using select would return all values where the block returns true for example:
p arr = ["abc", "d", "efg", "h", "abcde", "k"]
# => ["abc", "d", "efg", "h", "abcde", "k"]
p arr.each_index.select{|i| i >= 2 and arr[i].length == 1}
# => [3, 5]
Instead use detect if you want to return only the first value where the block returns true:
p arr = ["abc", "d", "efg", "h", "abcde", "k"]
# => ["abc", "d", "efg", "h", "abcde", "k"]
p arr.each_index.detect{|i| i >= 2 and arr[i].length == 1}
# => 3
Use Array#index with with_index:
arr = ["abc", "d", "efg", "h", "abcde"]
arr.index.with_index { |el, idx| el.length == 1 && idx > 2 }
=> 3
arr = ["abc", "d"]
arr.index.with_index { |el, idx| el.length == 1 && idx > 2 }
=> nil
def first_index_after(arr, min_ndx)
min_ndx.upto(arr.size-1).find { |i| yield(arr[i]) }
end
arr = ["abcdef", "h", "efgh", "h", "abcde", "h"]
min_ndx = 2
first_index_after(arr, min_ndx) { |e| e == "h" } #=> 3
first_index_after(arr, min_ndx) { |e| e =~ /\Ah\z/ } #=> 3
first_index_after(arr, min_ndx) { |e| e =~ /h/ } #=> 2
first_index_after(arr, min_ndx) { |e| e.size > 4 } #=> 4
first_index_after(arr, min_ndx) { |e| e == 'cat' } #=> nil
This assumes 0 <= min_ndx < arr.size. It may be desirable to add a first line to the method that returns nil or raises an exception if this requirement is not satisfied.
So I know how to add all the values in an array.
Example, the sum of [1,2,3,4]...
[1,2,3,4].inject(&:+)
#=> 10
However, I have an array of arrays and would like to add the values that have the same first element of each sub array.
# example
[["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]
Desired output:
"(A : 15) - (B : 10) - (C : 30)"
Any help would be appreciated!
arr = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]
h = arr.each_with_object(Hash.new(0)) { |(f,g),h| h[f] += g }
#=> {"A"=>15, "B"=>10, "C"=>30}
Then
h.map { |pair| "(%s : %s)" % pair }.join(" - ")
#=> "(A : 15) - (B : 10) - (C : 30)"
which you can combine like so:
arr.each_with_object(Hash.new(0)) { |(f,g),h| h[f] += g }.
map { |pair| "(%s : %s)" % pair }.join(" - ")
See Hash::new, especially with regards to the use of a default value (here 0).
Try this
arr = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]
arr.group_by(&:first).map { |key, group| [key, group.map(&:last).inject(:+)] }
# => [["A", 15], ["B", 10], ["C", 30]]
How does this work?
group_by(&:first) groups the subarrays by first element
map { |key, group| ... } transforms the groups
group.map(&:last).inject(:+) sums up all last elements in a group
a = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15], ["C", 15]]
result = a.group_by(&:first).each_with_object({}) do |(k, v), h|
h[k] = v.map(&:last).inject(:+)
# if your on Ruby 2.4+ you can write h[k] = v.sum(&:last)
end
#=> {"A"=>15, "B"=>10, "C"=>30}
Another option would be to build the hash from the beginning:
result = a.each_with_object({}) {|(k, v), h| h[k] = h[k].to_i + v }
#=> {"A"=>15, "B"=>10, "C"=>30}
If your desired output is literally a string "(A : 15) - (B : 10) - (C : 30)":
result.map { |k, v| "(#{k} : #{v})" }.join(' - ')
#=> "(A : 15) - (B : 10) - (C : 30)"
There are more elegant ways of doing this, but here is the solution as a block, so you can understand the logic...
What this does is :
convert the array to a hash, when combining
values.
Then it builds the string, one element at a time, storing
each in an array.
And finally, it combines the array of strings into your desired output.
'
my_array = [["A", 10],["A", 5],["B", 5],["B", 5],["C", 15],["C", 15]]
my_hash = {}
output_array = []
my_array.each do |item|
my_hash[item[0]] ||= 0
my_hash[item[0]] += item[1]
end
my_hash.each do |k,v|
output_array.push("(#{k} : #{v})")
end
puts output_array.join(" - ")