What does "<<" exactly do in ruby? - ruby-on-rails

I am new to Ruby, so I am still learning several things. But, I do have good experience with Java and C.
I would like to know what this does exactly:
[ 'a','b', 'c' ].each_with_index {|item, index| result << [item, index] }
Specifically, I am interested in the <<. Some research tells me that it is used for bit shifting, but it's obvious that is not the case here, so what is it doing here?

The << operator is adding items to the result array in this case.
See " how to add elements to ruby array (solved)".

In Ruby, all the things which are operators in C/Java, like +, -, *, /, and so on, are actually method calls. You can redefine them as desired.
class MyInteger
def +(other)
42 # or anything you want
end
end
Array defines the << method to mean "push this item on the end of this array". For integers, it's defined to do a bit shift.
Aside from Array, many other classes define << to represent some kind of "appending" operation.

It's the Array append operator.
<< is a method, and will do different things for different classes. Array uses it to push an object onto the end of an array. Fixnums use it to shift.

This is basically an Append Operator.
It was be used with to append either an element to an array or a substring to string
For Arrays
1.9.2-p290 :009 > arr = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
1.9.2-p290 :010 > arr << 6
=> [1, 2, 3, 4, 5, 6]
1.9.2-p290 :011 >
For Strings
1.9.2-p290 :011 > str = "ruby"
=> "ruby"
1.9.2-p290 :012 > str << 'rails'
=> "rubyrails"
1.9.2-p290 :013 >

Related

How to remove the outer array but still keep the inner elements in their arrays in a Rails SQL string?

I have this array:
array = [["1","one"], ["2","two"], ["3","three"]]
How do I remove the outer square brackets, removing one dimension from the array, so it looks like this:
array = ["1","one"], ["2","two"], ["3","three"]
I know if I wanted to flatten the entire array, so I get one large array, I could use flatten. However, I just want to get rid of the outer dimension, while keeping the inner elements within their respective arrays.
Essentially I am trying to get the query:
Phone.where(["brand_name in (?) AND os in (?) AND price_category in (?)", ["Nokia"], ["Blackberry", "Android"], ["1", "2"]])
But instead, this is what I am getting. Notice one more set of array brackets around the corresponding column values.
Phone.where(["brand_name in (?) AND os in (?) AND price_category in (?)", [["Nokia"], ["Blackberry", "Android"], ["1", "2"]]])
This is the method:
def self.checkbox_search(params_hash)
params_array = ["brand_name", "os", "price_category"]
column_array = []
key_array = []
params_hash.each do |k,v|
if params_array.include?(k)
column_array << v
key_array << k + ' in (?)'
end
end
joined_keys = key_array.join(" AND ") + ", " + "#{column_array}"
Phone.where([#{joined_keys}])
end
I am grabbing the params hash, and putting it in checkbox_search, which goes through the hash and puts the key values in key_array, and puts their values in column_array, if they meet specified criteria of key includes params_array. Then I join the entire string together in joined_keys, then put the results of joined_keys inside Phone.where() string
You're just assembling the arrays the wrong way:
Phone.where([ key_array.join(" AND ") ] + column_array)
That appends the column_array values. If you inline them then they'll be pushed down in terms of nesting. Note that #{...} has no place here, that's used for string interpolation and it will mess up things badly.
Technically the second version is equivalent to the first due to how it's parsed and assigned:
x = [1,2],[3,4]
# => [[1, 2], [3, 4]]
x
# => [[1, 2], [3, 4]]
That notation's normally used for situations like this:
x,y = [1,2],[3,4]
# => [[1, 2], [3, 4]]
x
# => [1, 2]
y
# => [3, 4]
There's no "outer dimension" you can remove. Either you have an array of arrays, or you have a singular array that's flat.
Reading your comment about the call to Phone you can try
array = [["1","one"], ["2","two"], ["3","three"]]
Phone.where(brand_name: array.map(&:last))
Your solution is very creative but you're greatly overcomplicating a simple task.
def self.checkbox_search(params_hash)
where(params_hash.slice(:brand_name, :os, :price_category))
end
If you only want certain keys from a hash you can use Hash#slice or for a params hash you can use ActionController::Parameters#permit.
There is absolutely no need to construct a SQL string manually. In ActiveRecord you can create WHERE ... AND ... conditions by:
Person.where(name: 'Max', awesome: true)
# or
Person.where(name: 'Max').where(awesome: true)
Passing an array as the value for a key creates a WHERE ... IN ...:
Person.where(name: ['Max', 'the12'])
See:
http://guides.rubyonrails.org/active_record_querying.html

Merge subarrays into single one and remove duplicates if they have same id using ruby on rails

I have an array of arrays like this:
array = [[1, 'Something', '123456321'], [2, 'Something', '123456321'], [2, 'Something', '1234563212']]
And I want to merge the subarrays that have same id and get this result:
array = [[1, 'Something', '123456321'], [2, 'Something, Something', '123456321, 1234563212']]
Can anyone help me? Thanks!
array.group_by(&:first).map do |id, records|
names = records.map(&:second).join(', ')
values = records.map(&:last).join(', ')
[id, names, values]
end
As you asked the reversed question recently, I suggest you to read the Enumerable, Array, Hash and String documentations. It will give you an instant boost in expressiveness and understanding of how to do common tasks with Ruby.
Just for fun, here's a one-liner :
array.group_by(&:first).map{|i, arrays| [i] + arrays.transpose.drop(1).map{|v| v.join(', ') } }

How to sort a hash by values

I was trying to sort a particular hash by values. I came across a way using the method sort_by. But even though I call sort_by on a hash, it returns an array, i.e.:
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
a.sort_by{|key, value| value}
# => [[2, "a"], [1, "b"], [0, "c"]]
If I try to convert the result into a hash, I end up with a hash sorted on key, hence the whole purpose of sort_by is lost. i.e.:
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
Hash[*a.sort_by{|key, value| value}.flatten]
# => {0=>"c", 1=>"b", 2=>"a"}
Is there a way I can sort a hash by value and yet get the results back in the form of a Hash?
I am using 1.8.6 ruby
You can use ActiveSupport::OrderedHash for Ruby 1.8:
ActiveSupport::OrderedHash implements a hash that preserves insertion order, as in Ruby 1.9
I don't have 1.8.6 running, but this should work:
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
ordered = ActiveSupport::OrderedHash[*a.sort_by{|k,v| v}.flatten]
ordered.keys
# => [2, 1, 0], this order is guaranteed
As noted in the quote above hashes in Ruby 1.9 "enumerate their values in the order that the corresponding keys were inserted", so this is only needed for Ruby 1.8.
A Hash is a collection of key-value pairs. It is similar to an Array,
except that indexing is done via arbitrary keys of any object type,
not an integer index. The order in which you traverse a hash by either
key or value may seem arbitrary, and will generally not be in the
insertion order.
Source: Ruby 1.8.7 docs
Hash preserves order. It enumerates its elements in the
Source: Ruby 1.9.1 changelog
If you want to use a ordered hash in Ruby 1.8, you should look at ActiveSupport's OrderedHash
You don't need to be in a Rails project, just include ActiveSupport in your project.
Your description is wrong.
a = {}
a[0] = "c"
a[1] = "b"
a[2] = "a"
Hash[*a.sort_by{|key, value| value}.flatten]
gives:
{
2 => "a",
1 => "b",
0 => "c"
}
which is sorted by value. By the way, your code is redundant. A better way to to this is:
Hash[a.sort_by{|_, value| value}]
or
Hash[a.sort_by(&:last)]
After Seeing Your Edit
You are using a version that does not have ordered hash. You cannot control the order of the elements. You cannot do anything with it.

Ruby "don't care variable" same as Prolog's? [duplicate]

This question already has answers here:
Where and how is the _ (underscore) variable specified?
(2 answers)
Closed 9 years ago.
So I'm both new to Prolog and Ruby. Learning Prolog at university and Ruby at my own. And I was thinking if there is a "don't care" or "trow away" variable in Ruby as there is in Prolog.
I just opened irb and just did this (supposing underscore was the "don't care" sign)
1.9.2-p290 :003 > _, b, c = [1,2,3]
=> [1, 2, 3]
1.9.2-p290 :004 > b
=> 2
1.9.2-p290 :005 > c
=> 3
The results are actually what I expected. But then I was curious about what where the value of underscore and what class it was
1.9.2-p290 :006 > _
=> 3
1.9.2-p290 :008 > _.class
=> Fixnum
Well, that's odd. Shouldn't it trow the value away? Why other value being stored?
Then doing more tests with underscore I saw what actually it was happening, it has the last evaluated value.
1.9.2-p290 :017 > 1
=> 1
1.9.2-p290 :018 > _
=> 1
1.9.2-p290 :019 > "string"
=> "string"
1.9.2-p290 :020 > _
=> "string"
1.9.2-p290 :021 > Hash
=> Hash
1.9.2-p290 :022 > _
=> Hash
So my question is: What's actually underscore for? Is it really a don't care variable or something else? What's the real name for it? (because I don't find many thing with "don't care ruby variable" with google)
What's throwing you is that you're seeing two different uses of the underscore.
In argument lists, it acts like a "don't care variable," like in Prolog.
Outside of argument lists, it's just a normal identifier. In IRB, it's bound to the previous result. Since your last input was c = 3, _ is 3. This is only in IRB, though — it doesn't happen in normal Ruby programs.
In the Ruby community, _ means "don't care".
In the Ruby language, _ doesn't mean anything, it's an identifier like any other.
In the YARV Ruby interpreter, the "unused local variable" warning is suppressed for _, thus encoding the convention in #1.
In IRb, _ is bound to the value of the last expression.
The underscore in Ruby acts like any normal variable, except it's a bit more special than that. It really does stand for "I don't care".
For, example, let's say you're looping through a array who's elements are 3-element arrays:
array = [[1,2,3],[4,5,6],[7,8,9],...]
Let's say you're only interested in the middle value. With _ you could do this:
array.each do |_, number, _|
# do something
end
If you try to do this with another variable, you will get the (expected) error that you duplicated a variable:
array.each do |v, number, v|
# do something
end
=> SyntaxError: (eval):2: duplicated argument name

Stop duplicates from being added to an array of Ruby objects

how can I eliminate duplicate elements from an array of ruby objects using an attribute of the object to match identical objects.
with an array of basic types I can use a set..
eg.
array_list = [1, 3, 4 5, 6, 6]
array_list.to_set
=> [1, 2, 3, 4, 5, 6]
can I adapt this technique to work with object attributes?
thanks
I think you are putting the cart before the horse. You are asking yourself: "How can I get uniq to remove objects which aren't equal?" But what you should be asking yourself, is: "Why aren't those two objects equal, despite the fact that I consider them to be?"
In other words: it seems you are trying to work around the fact that your objects have broken equality semantics, when what you really should do is simply fixing those broken equality semantics.
Here's an example for a Product, where two products are considered equal if they have the same type number:
class Product
def initialize(type_number)
self.type_number = type_number
end
def ==(other)
type_number == other.type_number
end
def eql?(other)
other.is_a?(self.class) && type_number.eql?(other.type_number)
end
def hash
type_number.hash
end
protected
attr_reader :type_number
private
attr_writer :type_number
end
require 'test/unit'
class TestHashEquality < Test::Unit::TestCase
def test_that_products_with_equal_type_numbers_are_considered_equal
assert_equal 2, [Product.new(1), Product.new(2), Product.new(1)].uniq.size
end
end
If you can write it into your objects to use eql? then you can use uniq.
what about uniq
a = [ "a", "a", "b", "b", "c" ]
a.uniq #=> ["a", "b", "c"]
you can use it on object as well!
Should you be using an Array, or should you be using a Set instead? If order isn't important, then the latter will make it more efficient to check for duplicates.
thanks for your responses.. uniq works once I added the following to my object model
def ==(other)
other.class == self.class &&
other.id == self.id
end
alias :eql? :==

Resources