This question already has answers here:
Why are exclamation marks used in Ruby methods?
(12 answers)
Closed 7 years ago.
a = [1,2,3]
a.uniq! # nil
a.uniq # [1,2,3]
Why a.uniq! is not [1,2,3] ?
Let me know the reason. Thank you!
You need to read the Ruby documentation.
The uniq method returns a new array by removing duplicate values in self. If no duplicates are found, the same array value is returned.
a = [ "a", "a", "b", "b", "c" ]
a.uniq # => ["a", "b", "c"]
b = [ "a", "b", "c" ]
b.uniq # => ["a", "b", "c"]
The uniq! method removes duplicate elements from self and returns nil if no changes are made (that is, no duplicates are found).
a = [ "a", "a", "b", "b", "c" ]
a.uniq! # => ["a", "b", "c"]
b = [ "a", "b", "c" ]
b.uniq! # => nil
most of the methods ending with bang (!) change the variable, while those without it just return the altered variable.
So, if you have something like this:
a = [1, 1, 2, 3]
a.uniq will return [1, 2, 3], but wont alter a, while a! will alter a to be equal to [1, 2, 3]
[1] pry(main)> a = [1,1,2,3]
=> [1, 1, 2, 3]
[2] pry(main)> a.uniq
=> [1, 2, 3]
[3] pry(main)> a
=> [1, 1, 2, 3]
[4] pry(main)> a.uniq!
=> [1, 2, 3]
[5] pry(main)> a
=> [1, 2, 3]
[6] pry(main)> a.uniq!
=> nil
[7] pry(main)> a
=> [1, 2, 3]
Related
My problem is that I need to do efficient lookups of if a 2 element array and their corresponding value is nil. So if I have the following arrays:
arr1 = [
[1, 2, 100],
[3, 4, nil],
[5, 6, 101]
]
I want something like
h = {
[1, 2] => 100,
[3, 4] => nil,
[5, 6] => 101
}
So I can do something like:
error = []
arr2 = [
[1,2],
[3,4],
[7,8]
]
arr2.each do |val|
if h.include?(val)
if h[val] == nil
error << "Value is nil"
else
# Do something
end
else
error << "Key doesn't exist"
end
end
Given that overwriting or ignoring duplicates is acceptable per your comment.
You can use Enumerable#each_with_object to iterate the Array and create a Hash like so
arr1 = [
[1, 2, 100],
[3, 4, nil],
[5, 6, 101],
[1, 2, nil],
]
arr1.each_with_object({}) do |(*first_two,last),obj|
obj[first_two] = last
end
#=> {[1, 2]=>nil, [3, 4]=>nil, [5, 6]=>101}
You can ignore duplicates in a similar fashion
arr1.each_with_object({}) do |(*first_two,last),obj|
obj[first_two] = last unless obj.key?(first_two)
end
#=> {[1, 2]=>100, [3, 4]=>nil, [5, 6]=>101}
Explanation:
each_with_object({}) will pass each element of of arr1 to the block along with an object (a Hash in this case)
(*first_two,last),obj - *first_two will collect everything up to last and obj is our Hash
obj[first_two] = last simple Hash key assignment
each_with_object returns the object (obj Hash in this case)
Update as recommended by #Stefan in ruby >= 2.7 you could also use
arr1.to_h {|*first_two,last| [first_two, last] }
This version will overwrite keys
arr1 = [
[1, 2, 100],
[3, 4, nil],
[5, 6, 101]
]
result = {}
arr1.each { |i| result[i.first(2)] = i.last }
=> {[1, 2]=>100, [3, 4]=>nil, [5, 6]=>101}
You can destructure every subarray during mapping and then convert result to hash with Array#to_h method
arr1 = [
[1, 2, 100],
[3, 4, nil],
[5, 6, 101],
[1, 2, nil],
]
arr1.map { |*first_two, last| [first_two, last] }.to_h
# => {[1, 2]=>nil, [3, 4]=>nil, [5, 6]=>101}
Duplicates will be overwritten
In case if you need for two last values as key:
arr.map { |b| { b.shift => b }.invert }
I have an array of the form [1,2,3,4,5].
Is it possible to loop through this array and during each iteration get an array where the starting point is the current element and the end point is the element before that?
Like
[1,2,3,4,5]
[2,3,4,5,1]
[3,4,5,1,2]
[4,5,1,2,3]
[5,1,2,3,4]
I am trying with .cycle method of array but it is not giving the expected result.
Check out rotate.
a = [ "a", "b", "c", "d" ]
a.rotate #=> ["b", "c", "d", "a"]
irb(main):005:0> array = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
irb(main):006:0> array.size.times.map{|i| array.rotate(i)}
=> [[1, 2, 3, 4, 5], [2, 3, 4, 5, 1], [3, 4, 5, 1, 2], [4, 5, 1, 2, 3], [5, 1, 2 , 3, 4]]
I have an array of strings. I'm wanting to change the name of these duplicate strings to append a numerical value to make them unique like so...
Original Array
a, a, A, b, c, D, d
Corrected Array
a, a1, A2, b, c, D, d1
I've gotten close to this with the following code; however, if the strings are a different case structure then they aren't currently considered duplicates with this code snippet. I would like them to be considered duplicates, but yet not change their case in the results array.
duplicate_counter = 1
duplicates = Array.new
duplicates = file_columns.select{ |e| file_columns.count(e) > 1 } # get duplicate column names
duplicates.each{ |x| file_columns.delete(x) }
duplicates.sort!
duplicates.each_with_index do |d, i|
if i > 0
if d == duplicates[i-1]
d = d.strip + duplicate_count.to_s
duplicate_count += 1
else
duplicate_count = 1
end
end
# Add back the column names, but with the appended numerical counts to make them unique
file_columns.push(d)
end
You are over thinking it considerably. I'm sure there are better ways to do this as well, but it gets the job done.
a = ['a', 'a', 'A', 'b', 'c', 'D', 'd']
letters = Hash.new(-1)
a.map do |letter|
l = letter.downcase
letters[l] += 1
if (letters[l] > 0)
"#{letter}#{letters[l]}"
else
"#{letter}"
end
end
Here's a way to do it if letters independent of case are not necessarily grouped. For example, it will convert this array:
arr = %w{ a D a A b c D a d }
#=> ["a", "D", "a", "A", "b", "c", "D", "a", "d"]
to:
["a", "D", "a1", "A2", "b", "c", "D1", "a3", "d2"]
Code
def convert(arr)
arr.each_with_index
.group_by { |c,_| c.downcase }
.values
.flat_map { |c|
c.map
.with_index { |(f,l),i| [i > 0 ? f<<i.to_s : f, l] } }
.sort_by(&:last)
.map(&:first)
end
Example
For arr above:
convert(arr)
#=> ["a", "D", "a1", "A2", "b", "c", "D1", "a3", "d2"]
Explanation
Dear reader, if you are new to Ruby, this may look impossibly complex. If you break it down into steps, however, it's not that bad. After you gain experience and become familiar with commonly-used methods, it will come quite naturally. Here I've used the following methods, chained together so that the return value of each becomes the receiver of the next:
Enumerable#each_with_index
Enumerable#group_by
Hash#values
Enumerable#flat_map
Enumerable#sort_by
Enumerable#first
Here's what's happening.
enum = arr.each_with_index
#=> #<Enumerator: ["a", "D", "a", "A", "b", "c",
# "D", "a", "d"]:each_with_index>
h = enum.group_by { |c,_| c.downcase }
#=> {"a"=>[["a", 0], ["a", 2], ["A", 3], ["a", 7]],
# "d"=>[["D", 1], ["D", 6], ["d", 8]],
# "b"=>[["b", 4]],
# "c"=>[["c", 5]]}
a = h.values
#=> [[["a", 0], ["a", 2], ["A", 3], ["a", 7]],
# [["D", 1], ["D", 6], ["d", 8]],
# [["b", 4]],
# [["c", 5]]]
b = a.flat_map { |c| c.map.with_index { |(f,l),i| [i > 0 ? f<<i.to_s : f, l] } }
#=> [["a", 0], ["a1", 2], ["A2", 3], ["a3", 7], ["D", 1],
# ["D1", 6], ["d2", 8], ["b", 4], ["c", 5]]
c = b.sort_by(&:last)
#=> [["a", 0], ["D", 1], ["a1", 2], ["A2", 3], ["b", 4],
# ["c", 5], ["D1", 6], ["a3", 7], ["d2", 8]]
c.map(&:first)
#=> ["a", "D", "a1", "A2", "b", "c", "D1", "a3", "d2"]
for slicing an array, we can use
2.0.0p247 :021 > arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
2.0.0p247 :022 > arr.each_slice(3).to_a
=> [[1, 2, 3], [4, 5]]
2.0.0p247 :034 > arr # does not change array
=> [1, 2, 3, 4, 5]
i want to take only first part of sliced array, so i did it in the following way
2.0.0p247 :029 > arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
2.0.0p247 :030 > arr[0..2]
=> [1, 2, 3]
2.0.0p247 :031 > arr # does not change array
=> [1, 2, 3, 4, 5]
but it return a new array, and i want to do it in such a way that i can take a part of array in the same array without creating a new array
As in Ruby there are some methods of changing same array by putting a '!' sign as - sort!,reject! etc
Is there any method of doing this?
Given
array = [1,2,3,4,5]
To return array=[1,2,3], you can:
Slice! off the last half, so you return the first half.
array.slice!(3..5)
Return the first three elements and assign it to the variable.
array = array.first 3
Or
array = array[0..2]
...Or use a number of other array methods.
Do you mean slice! as found in the ruby docs?:
1.9.3p392 :001 > ar = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
1.9.3p392 :002 > ar.slice!(0,2)
=> [1, 2]
1.9.3p392 :003 > ar
=> [3, 4, 5]
I need to parse and display solr facets which are returned in either JSON or Ruby formar:
Collections: [ "a", 1, "b", 2, "c", 3, "d", 4, ... ]
into
{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
What is the cleanest way?
EDIT: Well now that we know what you actually want, a hash ...
collections = ["a", 1, "b", 2, "c", 3, "d", 4]
Hash[*collections]
# => {"a"=>1, "b"=>2, "c"=>3, "d"=>4}
Original answer: I may not understand your goal but...
collections = ["a", 1, "b", 2, "c", 3, "d", 4]
collections.each_slice(2).map{ |(x, y)| "#{x} - #{y}" }
# => ["a - 1", "b - 2", "c - 3", "d - 4"]
What i see you want to do is maybe a hash ? {a => "1", b => "2"} ??
If so, read below:
collections = [ "a", 1, "b", 2, "c", 3, "d", 4]
result = Hash[*collections.flatten]
result prints {"a"=>1, "b"=>2, "c"=>3, "d"=>4}