How to analyze the "max" method - ruby

Can someone explain why "time" is the max value here?
my_array = %w{hello my time here is long}
my_array.max #=> "time"

Because alphabetically t in time is greater here among others in your array my_array.
Here is one way,how string comparisons happened :
'hello' > 'time' # => false
'my' > 'time' # => false
'here' > 'time' # => false
'is' > 'time' # => false
'long' > 'time' # => false
To understand the outputs of the above fragment code,you must need to see String#<=> documentation. As your my_array contains all string instances,which has called the method <=>,to build the output of max.
Documentations says Enumerable#max:
Enumerable#max,without block assumes all objects implement Comparable.

Here's how computers look at the strings and compare them.
If we look at the first characters of each word it'll help a little, because we know how the alphabet orders letters:
%w[hello my time here is long].map{ |s| s[0] }.sort # => ["h", "h", "i", "l", "m", "t"]
But that doesn't really help visualize it, so here's a look at each word's letters as a computer sees them:
%w[time tome].each do |w|
puts w.chars.map(&:ord).join(', ')
end
# >> 116, 105, 109, 101
# >> 116, 111, 109, 101
Each letter has a value. Over the years there have been many different ways of ordering letters for a computer, which caused the character to value mapping to change. EBCDIC and ASCII have been the most popular but have different orders. We're usually dealing with ASCII, or a derivative, which is set by the OS.
Look at how the characters in the words are represented by the values in the following output. It should make it easy to understand what the computer is doing then.
%w[he hello help holler hollow].sort.each do |w|
puts '"%6s": %s' % [ w, w.chars.map(&:ord).join(', ') ]
end
# >> " he": 104, 101
# >> " hello": 104, 101, 108, 108, 111
# >> " help": 104, 101, 108, 112
# >> "holler": 104, 111, 108, 108, 101, 114
# >> "hollow": 104, 111, 108, 108, 111, 119

Related

Can't encipher text in Ruby. It showes me the last letter of cipher-key in all my plaintext after iterating

Can't encipher text in Ruby. It showes me the last letter of cipher-key in all my plaintext after iterating.
key is: VCHPRZGJNTLSKFBDQWAXEUYMOI
plaintext is: Hello, CS-50!
expected ciphered_text: Jrssb, HA-50!
I got ciphered_text: Iiiii, II-50!
I don't know why I got a last letter of a key (I) in every char of ciphered_text....
Maybe I need a "break" after every succes "if". But it doesn't helped.
Here is my code:
# Design and implement a program, substitution, that encrypts messages using a substitution cipher.
plaintext_str = 'Hello, CS-50!'
key_str = 'VCHPRZGJNTLSKFBDQWAXEUYMOI'
# Converting string into array:
plaintext = plaintext_str.split('')
key = key_str.split('')
# Check if letter is alphabetical
def alpha?(char)
char.match?(/^[[:alpha:]]$/)
end
# Check if letter is in uppercase
def upper?(char)
char.match?(/^[[:upper:]]$/)
end
# # Check if letter is in lowercase
def lower?(char)
char.match?(/^[[:lower:]]$/)
end
# ASCII arrays value assigned to capital letters for alphabets
capital_letters = [65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90]
# ASCII arrays value assigned to small letters for alphabets
small_letters = [97,
98,
99,
100,
101,
102,
103,
104,
105,
106,
107,
108,
109,
110,
111,
112,
113,
114,
115,
116,
117,
118,
119,
120,
121,
122]
# Define variable for ciphertext:
ciphertext = ''
# iterating each plaintext's char (i-th):
plaintext.each_index do |i|
# iterating each key's char j-th on i-th char of plaintext:
key.each_index do |j|
# If char in plaintext is alphabetical:
if alpha?(plaintext[i])
# Check if letter is in uppercase
if upper?(plaintext[i])
capital_letters.each_index do |k|
# Checking if plaintext's letter is equal to alphabet's letter in [j]
if plaintext[i].ord == capital_letters[k]
ciphertext[i] = key[j].upcase
break
end
end
# Check if letter is in lowercase
elsif lower?(plaintext[i])
small_letters.each_index do |l|
if plaintext[i].ord == small_letters[l]
ciphertext[i] = key[j].downcase
break
end
end
end
# if non-alphabetical:
else
ciphertext[i] = plaintext[i]
end
end
end
puts "ciphertext: #{ciphertext}"
As a preface, the code you have here looks an awful lot like C. There are a lot of concepts that Ruby provides which can make problems like this into significantly shorter solutions. For now, we'll focus on what's wrong with your code as it is.
The main issue is you have three index variables, i from plaintext, j from key, and k from capital_letters. You check that if plaintext[i] == capital_letters[k], then you place key[j] at the ith position. But, since j was never participating in the checks, you will simply pass this check for all indices in your key variable. So, you might as well have ciphertext[i] = key.last.upcase, and if you check key, the last entry is I, hence why your output is nothing but I's.
Some suggestions for how you could simplify your code:
def encipher(plaintext, key)
# Make a table that maps { plaintext character => target character }
# With your input example, table = {"A" => "V", "B" => "C", "C" => "H", ... }
table = key.chars
.each_with_index
.map { |sub, index| [Array("A".."Z")[index], sub] }
.to_h
plaintext.map { |char|
# TODO: Using char and table, encipher exactly one character
}.join
end

Ruby Byte XOR strange result - help please

I was doing some XOR of data and things were going well with my hex based XOR. It was recommend that I use a byte XOR (^) and only work with bytes. I thought that will take no time to change that but I have the some strange behaviour that I had not expected.
Could some add a little light as to why I'm getting a different result if I'm processing the string as bytes. I was expecting it to be the same.
m_hex_string ="124f33e6a118566377f237075354541f0a5a1b"
m_XOR_string ="662756c6c27732065796586974207468652870"
m_expected ="the code don't work"
m_expected_hex ="74686520636f646520646f6e277420776f726b"
def XOR_hex_strings(a,b)
(a.hex ^ b.hex).to_s(16)
end
def XOR_byte_strings(s1,s2)
xored = s1.bytes.zip(s2.bytes).map { |(a,b)| a ^ b }.pack('c*')
end
def hex_digest(hexdigest)
[hexdigest].pack("H*")
end
puts "My strings for stack overflow"
puts "'"+hex_digest(XOR_hex_strings(m_hex_string,m_XOR_string))+"'"
puts "'"+hex_digest(XOR_byte_strings(m_hex_string,m_XOR_string))+"'"
Results:
My strings for stack overflow
'the code don't work'
'tje`#ode ?on't ~mrk'
The text should be the same 'the code don't work' for both methods. I'd really like to know why rather than just a correct code fragment. thanks.
As already said in the comments, bytes doesn't take the hex format into account, it just returns the integer values for "1", "2", "4", "f" etc. You can convert the hex string with pack:
[m_hex_string].pack("H*")
# => "\x12O3\xE6\xA1\x18Vcw\xF27\aSTT\x1F\nZ\e"
unpack converts this into a byte array, just like bytes but more explicit and faster (IIRC):
[m_hex_string].pack("H*").unpack("C*")
# => [18, 79, 51, 230, 161, 24, 86, 99, 119, 242, 55, 7, 83, 84, 84, 31, 10, 90, 27]
The final method would look like:
def XOR_pack_unpack_strings(s1, s2)
s1_bytes = [s1].pack("H*").unpack("C*")
s2_bytes = [s2].pack("H*").unpack("C*")
s1_bytes.zip(s2_bytes).map { |a, b| a ^ b }.pack('C*')
end
If speed is an issue, take a look at the fast_xor gem:
require 'xor'
def XOR_fast_xor_strings(s1_hex, s2_hex)
s1 = [s1_hex].pack("H*")
s2 = [s2_hex].pack("H*")
s1.xor!(s2)
end

Remove "\n\" from string

Is there any way to remove "\r\" from string?
Sofar i manage to remove only "\r" with mystring.gsub(/\r/,"")
How do I remove all 3 characters \r\ ?
In your string, do you have the literal characters "\" and "r", or do you have the escape sequence "\r"?
If you have the string foo\r\fbar, then your string is 8 characters long:
"foo\r\fbar".split(//).map(&:ord)
=> [102, 111, 111, 13, 12, 98, 97, 114]
What you want to remove are the \r and \f characters, or character codes 13 and 12. You can't remove just the leading slash in the \f, because \f is just one character. If this is your case, you can use:
"foo\r\fbar".gsub(/\r\f/, "")
=> "foobar"
However, if you have the literal sequence foo\\r\\fbar:
"foo\\r\\fbar".split(//).map(&:ord)
=> [102, 111, 111, 92, 114, 92, 102, 98, 97, 114]
Then you can remove the \r and the slash before the "f":
"foo\\r\\fbar".gsub(/\\r\\/, "")
=> "foofbar"
If you have the sequence foo\r\\fbar:
"foo\r\\fbar".split(//).map(&:ord)
=> [102, 111, 111, 13, 92, 102, 98, 97, 114]
Then you can likewise remove the \r and the slash before the "f":
"foo\r\\fbar".gsub(/\r\\/, "")
=> "foofbar"
Use this:
mystring.gsub(/\r\\/,"")
mystring.gsub(/\\r\\/,"")
Ivaylo was so close :P
I tested it on http://rubular.com/
As you can tell, it's difficult for us to figure out what characters you actually need to remove, because you are contradicting yourself. In the title you say "\n\" and in the question you say "\r\". Either way, here's what I'd do in order to find out exactly what I need to search for.
Starting with the string in question:
mystring = "\n\\"
I'd use the bytes method to have Ruby show me what I should use:
mystring = "\n\\" # => "\n\\"
mystring.bytes.map{ |b| '%02x' % b } # => ["0a", "5c"]
mystring.tr("\x0a\x5c", '') # => ""
mystring.gsub(/\x0a\x5c/, '') # => ""
mystring = "\r\\" # => "\r\\"
mystring.bytes.map{ |b| '%02x' % b } # => ["0d", "5c"]
mystring.tr("\x0d\x5c", '') # => ""
mystring.gsub(/\x0d\x5c/, '') # => ""
Dealing with escaped characters can be a pain in any programming language, but if I look at the exact bytes that make up the character I can't go wrong, as long as I'm dealing with ASCII. If it's another character set, I'll want to use the chars method, and adjust my pattern appropriately:
mystring = "\n\\"
mystring.chars.to_a # => ["\n", "\\"]
mystring.gsub(/\n\\/, '') # => ""
mystring.tr("\n\\", '') # => ""
mystring = "\r\\"
mystring.chars.to_a # => ["\r", "\\"]
mystring.tr("\r\\", '') # => ""
mystring.gsub(/\r\\/, '') # => ""

Ruby 1.9.3 Dir.glob not returning NFC UTF-8 strings, returns NFD instead

When reading file names from Ruby 1.9.3, I'm seeing some odd results. For example with the following test ruby script, running in a folder containing a file with the name 'Testé.txt'
#!encoding:UTF-8
def inspect_string s
puts "Source encoding: #{"".encoding}"
puts "External encoding: #{Encoding.default_external}"
puts "Name: #{s.inspect}"
puts "Encoding: #{s.encoding}"
puts "Chars: #{s.chars.to_a.inspect}"
puts "Codepoints: #{s.codepoints.to_a.inspect}"
puts "Bytes: #{s.bytes.to_a.inspect}"
end
def transform_string s
puts "Testing string #{s}"
puts s.gsub(/é/u,'TEST')
end
Dir.glob("./*.txt").each do |f|
puts RUBY_VERSION + RUBY_PLATFORM
puts "Inline string works as expected"
s = "./Testé.txt"
inspect_string s
puts transform_string s
puts "File name from Dir.glob does not"
inspect_string f
puts transform_string f
end
On Mac OS X Lion, I see the following results:
1.9.3x86_64-darwin11.4.0
Inline string works as expected
Source encoding: UTF-8
External encoding: UTF-8
Name: "./Testé.txt"
Encoding: UTF-8
Chars: [".", "/", "T", "e", "s", "t", "é", ".", "t", "x", "t"]
Codepoints: [46, 47, 84, 101, 115, 116, 233, 46, 116, 120, 116]
Bytes: [46, 47, 84, 101, 115, 116, 195, 169, 46, 116, 120, 116]
Testing string ./Testé.txt
./TestTEST.txt
File name from Dir.glob does not
Source encoding: UTF-8
External encoding: UTF-8
Name: "./Testé.txt"
Encoding: UTF-8
Chars: [".", "/", "T", "e", "s", "t", "e", "́", ".", "t", "x", "t"]
Codepoints: [46, 47, 84, 101, 115, 116, 101, 769, 46, 116, 120, 116]
Bytes: [46, 47, 84, 101, 115, 116, 101, 204, 129, 46, 116, 120, 116]
Testing string ./Testé.txt
./Testé.txt
The expected last line is
./TestTEST.txt
the encodings returned indicate that this is a normal UTF-8 string and yet any regexp transformations involving unicode are not being applied properly.
An update to this: Ruby 2.2.0 has gained String#unicode_normalize.
f.unicode_normalize!
would convert the NFD-decomposed string returned from OSX' HFS+ filesystem into a NFC-composed string. You can specify :nfd, :nfkc, or :nfkd if you require alternative normalizations.
Posted in case this is useful for anyone else running into this:
Ruby 1.9 and 2.0 will use composed UTF-8 strings if you use UTF-8 encoding, but will not modify strings received from the OS. Mac OS X uses decomposed strings (two bytes for many common accents like é in UTF-8, which are combined for display). So file system methods will often return unexpected string formats, which are strictly UTF-8, but a decomposed form.
In order to work around this, you need to decompose them by converting from the 'UTF8-MAC' encoding to UTF-8:
f.encode!('UTF-8','UTF8-MAC')
Before using them, otherwise you may end up running checks against a decomposed string with a native ruby string which is composed.
This behaviour affects all file system calls like glob for both files and folders where a file name contains unicode characters.
Apple docs:
http://developer.apple.com/library/mac/#qa/qa1235/_index.html

Ruby: character to ascii from a string

this wiki page gave a general idea of how to convert a single char to ascii http://en.wikibooks.org/wiki/Ruby_Programming/ASCII
But say if I have a string and I wanted to get each character's ascii from it, what do i need to do?
"string".each_byte do |c|
$char = c.chr
$ascii = ?char
puts $ascii
end
It doesn't work because it's not happy with the line $ascii = ?char
syntax error, unexpected '?'
$ascii = ?char
^
The c variable already contains the char code!
"string".each_byte do |c|
puts c
end
yields
115
116
114
105
110
103
puts "string".split('').map(&:ord).to_s
Ruby String provides the codepoints method after 1.9.1.
str = 'hello world'
str.codepoints
=> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
str = "你好世界"
str.codepoints
=> [20320, 22909, 19990, 30028]
use "x".ord for a single character or "xyz".sum for a whole string.
please refer to this post for the changes in ruby1.9 Getting an ASCII character code in Ruby using `?` (question mark) fails
You could also just call to_a after each_byte or even better String#bytes
=> 'hello world'.each_byte.to_a
=> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
=> 'hello world'.bytes
=> [104, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100]
"a"[0]
or
?a
Both would return their ASCII equivalent.

Resources