Why isn't there a String#shift()? - ruby

I'm working my way through Project Euler, and ran into a slightly surprising omission: There is no String#shift, unshift, push, or pop. I had assumed a String was considered a "sequential" object like an Array, since they share the ability to be indexed and iterated through, and that this would include the ability to easily change the beginning and ends of the object.
I know there are ways to create the same effects, but is there a specific reason that String does not have these methods?

Strings don't act as an enumerable object as of 1.9, because it's considered too confusing to decide what it'd be a list of:
A list of characters / codepoints?
A list of bytes?
A list of lines?

Not being a Ruby contributor, I can't speak to their design goals, but from experience, I don't think that strings are regarded as 'sequential' objects; they're mutable in ways that suggest sequential behaviour, but most of the time they're treated atomically.
Case in point: in Ruby 1.9, String no longer mixes in Enumerable.

>> mystring = "abcdefgh"
=> "abcdefgh"
>> myarray = mystring.split("")
=> ["a", "b", "c", "d", "e", "f", "g", "h"]
>> myarray.pop
=> "h"
>> mystring = myarray.join
=> "abcdefg"
this should do it, you wouldhave to convert it to an array, and then back though
UPDATE:
use String#chop! and Stirng#<<
>> s = "abc"
=> "abc"
>> s.chop!
=> "ab"
>> s
=> "ab"
>> s<<"def"
=> "abdef"
>> s
=> "abdef"
>>

Well at least in 1.9.2, you can deal with a string like an array.
ruby-1.9.2-p290 :001 > "awesome"[3..-1] => "some"
So if you want to do a sort of character left shift, just use [1..-1]
ruby-1.9.2-p290 :003 > "fooh!"[1..-1] => "ooh!"

Related

Why is a hash literal called a hash literal in Ruby?

This is probably something you learn in programming 101.
Disclaimer: I have no formal programming training. I am self-taught.
For me, literal hash is like what this website suggests: a third editable hash called "corned beef hash".
In Ruby, you have two data types:
hash
hash literals
Why is one called a literal? Is it because you literally type out the associative array? The website above claims it is because the definition is inline. If so, why is the hash not also called literal when you can type it out like this:
states = Hash.new
states["CA"] = "California"
states["MA"] = "Massachusetts"
states["NY"] = "New York"
states["MA"].reverse #=> "sttesuhcassaM"
The data type is just one: Hash. Hash is a class. You can instantiate objects and use them
h = Hash.new
h.store("CA", "California")
h["MA"] = "Massachusetts"
A literal is just a shortcut which let you create objects of that class without explicitly use that class.
h = { "CA" => "California", "MA" => "Massachusetts" }
Same for Arrays
a = Array.new
a.push(1)
a << 2
Or, with array literal
a = [1, 2]
Your confusion stems from this misconception:
In Ruby, you have two data types:
hash
hash literals
Firstly, there are many more data structures in the Ruby core.
But secondly, there is no such thing as "literal hash". Hash literals refer to syntax sugar for defining hashes in place, aka literally.
# This is a hash literal
x = {foo: 42, bar: :baz}
# This is not a hash literal
x = Hash.new
x[:foo] = 42
x[:bar] = :baz
They are completely identical. The only difference is one is more convenient, while the other is more dynamic.
A literal is a fixed value.
It cannot be edited, unless you assign it to a variable and then modify that (although then of course you are not actually modifying the literal).
https://en.wikipedia.org/wiki/Literal_(computer_programming)
So you can assign a literal to a variable, compare a variable to a literal, compare two literals, but you cannot in general modify a literal directly.
Edit: Note that cases where a literal is modified turn out to be creating a new object, unlike the same operation performed on a variable.
2.2.5 :001 > "foo".upcase!
=> "FOO"
2.2.5 :002 > "foo".object_id
=> 2204993280
2.2.5 :003 > "foo".upcase!.object_id
=> 2204964760
2.2.5 :004 > x = "foo"
=> "foo"
2.2.5 :005 > x.object_id
=> 2204927520
2.2.5 :006 > x.upcase!.object_id
=> 2204927520
2.2.5 :007 >

How to sort! arrays in ruby

I want to sort my_array and then reverse the order.
Which markup is correct?
my_array.sort.reverse!
or
my_array.sort!.reverse
Or does it make any difference?
Thanks
You have to decompose the chain :
First, let's understand the difference between the sort and the sort! method.
If I write
array = [7,2,4]
array.sort!
array # => [2,4,7]
If you write
array = [7,2,4]
foo = array.sort
array # => [7,2,4]
foo # => [2,4,7]
The sort method sort the array and returns the result as the output of the function, whereas the sort! one directly modifies the existing array.
So if you write :
my_array.sort.reverse!
It is like writing :
(my_array.sort). # => Here we create a new array who is calculated by sorting the existing one
reverse! # => Then we reverse this new array, who is not referenced by a variable.
If you write :
(my_array.sort!). #=> Here you sort my_array and reinject the result into my_array !
reverse # Then reverse it and inject the result into a NEW array
So in both cases, you will not obtain what you want ! What you want to do is either :
my_array.sort!.reverse!
or :
new_array = my_array.sort.reverse
You'll get the same output, but one will modify the initial array. See this:
2.1.1 :001 > my_array = ["a","d","b","c"]
=> ["a", "d", "b", "c"]
Just declaring an array with a, b, c, and d in completely wrong orders.
2.1.1 :002 > my_array.sort.reverse!
=> ["d", "c", "b", "a"]
Running your first command on it returns a reverse-sorted array
2.1.1 :003 > my_array
=> ["a", "d", "b", "c"]
... but doesn't modify the original array itself.
2.1.1 :004 > my_array.sort!.reverse
=> ["d", "c", "b", "a"]
Running the second command returns the same result, the array sorted backwards
2.1.1 :005 > my_array
=> ["a", "b", "c", "d"]
But the array itself has been modified, but only by the sort! call. A ! after a method call 'saves' the changes to the object it's called on and returns the result. So in the second one:
You sort the array, saving the changes to it and returning the sorted array, then
Run reverse on the sorted array, which doesn't save to anything and only returns the result.
my_array.sort!.reverse will modify the receiver so my_array will be sorted after this call e.g.
my_array = [1,4,3,5,2]
my_array.sort!.reverse
#=> [5,4,3,2,1]
my_array
#=> [1,2,3,4,5]
the second form my_array.sort.reverse! will not modify my_array because sort will dup the array first then reverse! will modify this duped copy which is not being stored. This would have the same impact as my_array.sort.reverse without the !
my_array = [1,4,3,5,2]
my_array.sort.reverse!
#=> [5,4,3,2,1]
my_array
#=> [1,4,3,5,2]
Thirdly, something like my_array.sort!.reverse! will modify the receiver twice meaning my_array.sort! will sort the array in place and then .reverse! will reverse the array in place. so my_array will now be sorted and reversed.
my_array = [1,4,3,5,2]
my_array.sort!.reverse!
#=> [5,4,3,2,1]
my_array
#=> [5,4,3,2,1]
Although in this case I do not think you will need either bang ! method as the second form has no impact. bang ! in ruby means "dangerous", may alter the data or have other unexpected results.
my_array.sort!.reverse!
is the correct answer, since you want to change the array you already have.
my_array.sort.reverse!
creates a sorted copy and reverses it (the original doesn't change).
my_array.sort!.reverse
sorts the original and creates a reversed copy (the original isn't reversed).

How might I match a string in ruby without using regular expressions?

Currently, I'm doing this:
(in initialize)
#all = Stuff.all.each.map {|t| t.reference_date }
#uniques = #all.uniq
results = []
#uniques.each do |k|
i = 0
#all.each do |x|
i += 1 if x =~ %r{#{x}}
end
results << [k, i]
end
And that's fine. It's going to work. But I like to avoid regular expressions when I can. I think they are a bit feo. That's spanish for ugly.
EDIT--
actually, that's not working because ruby "puts" the date as a numbered format like 2012-03-31 when the date object is placed inside of a string (as a variable, here), but its really a date object, so this worked:
if x.month == k.month && x.day == k.day
i += 1
end
You can do it with just 1 line (if I got right the question of course):
array = %w(a b c d a b d f t z z w w)
# => ["a", "b", "c", "d", "a", "b", "d", "f", "t", "z", "z", "w", "w"]
array.uniq.map{|i|[i, array.count(i)]}
# => [["a", 2], ["b", 2], ["c", 1], ["d", 2], ["f", 1], ["t", 1], ["z", 2], ["w", 2]]
results = Hash.new(0)
#all.each{|t| results[t] += 1}
# stop here if a hash is good enough.
# if you want a nested array:
results = results.to_a
This is the standard way of getting the frequency of elements in an enumerable.
Something you can do to avoid the appearance of regular expressions, is to build them on the fly using Regexp.union. The reason you might want to do this is SPEED. A well constructed regex is faster than iterating over a list, especially a big one. And, by allowing your code to build the regex, you don't have to maintain some ugly (feo) thing.
For instance, here's something I do in different chunks of code:
words = %w[peer_address peer_port ssl ssl_protocol ssl_key_exchange ssl_cipher]
regex = /\b(?:#{ Regexp.union(words).source })\b/i
=> /\b(?:peer_address|peer_port|ssl|ssl_protocol|ssl_key_exchange|ssl_cipher)\b/i
That makes it trivial to maintain a regex. And, try a benchmark using that to find substrings in text against iterating and it'll impress you.
If wildcards will work for you, try File.fnmatch
From your code I sense you want to get the number of occurrence of each reference_date. This can be achieved much easier by using ActiveRecord and SQL directly instead of pulling the whole tale and then performing time consuming operations in Ruby.
If you are using Rails 2.x you can use something like this:
Stuff.find(:all, :select => "reference_date, COUNT(*)", :group => "reference_date")
or if you are using Rails 3 then you can simplify it to
Stuff.count(:group => "reference_date")

How to split a string containing both delimiter and the escaped delimiter?

My string delimiter is ;. Delimiter is escaped in the string as \;. E.g.,
irb(main):018:0> s = "a;b;;d\\;e"
=> "a;b;;d\\;e"
irb(main):019:0> s.split(';')
=> ["a", "b", "", "d\\", "e"]
Could someone suggest me regex so the output of split would be ["a", "b", "", "d\\;e"]? I'm using Ruby 1.8.7
1.8.7 doesn't have negative lookbehind without Oniguruma (which may be compiled in).
1.9.3; yay:
> s = "a;b;c\\;d"
=> "a;b;c\\;d"
> s.split /(?<!\\);/
=> ["a", "b", "c\\;d"]
1.8.7 with Oniguruma doesn't offer a trivial split, but you can get match offsets and pull apart the substrings that way. I assume there's a better way to do this I'm not remembering:
> require 'oniguruma'
> re = Oniguruma::ORegexp.new "(?<!\\\\);"
> s = "hello;there\\;nope;yestho"
> re.match_all s
=> [#<MatchData ";">, #<MatchData ";">]
> mds = re.match_all s
=> [#<MatchData ";">, #<MatchData ";">]
> mds.collect {|md| md.offset}
=> [[5, 6], [17, 18]]
Other options include:
Splitting on ; and post-processing the results looking for trailing \\, or
Do a char-by-char loop and maintain some simple state and just split manually.
As #dave-newton answered, you could use negative lookbehind, but that isn't supported in 1.8. An alternative that will work in both 1.8 and 1.9, is to use String#scan instead of split, with a pattern accepting not (semicolon or backslash) or anychar prefixed by backlash:
$ irb
>> RUBY_VERSION
=> "1.8.7"
>> s = "a;b;c\\;d"
=> "a;b;c\\;d"
s.scan /(?:[^;\\]|\\.)+/
=> ["a", "b", "c\\;d"]

Why is this Ruby 1.9 code resulting in an empty hash?

I'm trying to zip 3 arrays into a hash. The hash is coming up empty, though. Here's sample code to reproduce using Ruby 1.9:
>> foo0 = ["a","b"]
=> ["a", "b"]
>> foo1 = ["c","d"]
=> ["c", "d"]
>> foo2 = ["e", "f"]
=> ["e", "f"]
>> h = Hash[foo0.zip(foo1, foo2)]
=> {}
I'd like to zip these and then do something like:
h.each_pair do |letter0, letter1, letter2|
# process letter0, letter1
end
It's not clear what you expect the output to be but the [] operator of the Hash class is intended to take an even number of arguments and return a new hash where each even numbered argument is the key for the corresponding odd numbered value.
For example, if you introduce foo3 = ["d"] and you want to get a hash like {"a"=>"b", "c"=>"d"} you could do the following:
>> Hash[*foo0.zip(foo1, foo2, foo3).flatten]
=> {"a"=>"b", "c"=>"d"}
Hash[] doesn't work quite like you're assuming. Instead, try this:
>> Hash[*foo0, *foo1, *foo2]
=> {"a"=>"b", "c"=>"d", "e"=>"f"}
or, my preferred approach:
>> Hash[*[foo0, foo1, foo2].flatten]
=> {"a"=>"b", "c"=>"d", "e"=>"f"}
Basically, Hash[] is expecting an even number of arguments as in Hash[key1, val1, ...]. The splat operator * is applying the arrays as arguments.
It looks like foo0.zip(foo1,foo2) generates:
[["a", "b", "c"]]
Which is not an acceptable input for Hash[]. You need to pass it a flat array.
you don't need Hash for what you are trying to accomplish, zip does it for you
foo0.zip(foo1, foo2) do |f0, f1, f2|
#process stuff here
end

Resources