Iterate over an array of characters in steps - ruby

I have an array of hex characters that is > 8000 characters, and I need to do some operation on every 6 characters in the array.
Ranges in ruby have a really cool step feature:
(1..100).step(6) do //etc....
Is there any kind of functionality similar to this for arrays?
Something like:
string.split("").step(6) do //etc...

You want Enumerable#each_slice:
require 'enumerator' # if pre-Ruby1.9
string.split("").each_slice(6) do |ary|
# ary is a 6-length array, and this is executed for every block of 6 characters
end

You say that you have an array of characters, but then you show code using string.split("").
More efficient than using split("")—which will create an intermediary array of 8,000 strings before beginning, wasting both time and memory—use the String#chars enumerator along with each_slice:
string.chars.each_slice(6) do |a,b,c,d,e,f|
# one variable for each character
end
or
string.chars.each_slice(6) do |a|
# a is an array of all six
end
(Also note each_cons(6), in case that's what you really meant.)

Use Array.each_slice(6):
%w[a b c d e f g h i j k l m n o p].each_slice(6) { |s| puts s.join(',') }
a,b,c,d,e,f
g,h,i,j,k,l
m,n,o,p

You could just use a range with step, and Array's method values_at. This method takes one or more integers, or a range as indices argument. Unfortunately Range's step method doesn't return a range, but an enumerable. This can however simply be converted to an array of integers by prefixing an asterisk *.
string = "You could just use a range with step, and Array's method values_at."
step6 = (0...string.length).step(6)
string.split("").values_at(*step6).each do |char|
puts char
end

Related

Given a string, how do I compare the characters to see if there are duplicates?

I'm trying to compare characters in a given string to see if there are duplicates, and if there are I was to remove the two characters to reduce the string to as small at possible. eg. ("ttyyzx") would equal to ("zx")
I've tried converting the characters in an array and then using an #each_with_index to iterate over the characters.
arr = ("xxyz").split("")
arr.each_with_index do |idx1, idx2|
if idx1[idx2] == idx1[idx2 + 1]
p idx1[idx2]
p idx1[idx2 + 1]
end
end
At this point I just wan to be able to print the next character in the array within the loop so I know I can move on to the next step, but no matter what code I use it will only print out the first character "x".
To only keep the unique characters (ggorlen's answer is "b"): count all characters, find only those that appear once. We rely on Ruby's Hash producing keys in insertion order.
def keep_unique_chars(str)
str.each_char.
with_object(Hash.new(0)) { |element, counts| counts[element] += 1 }.
select { |_, count| count == 1 }.
keys.
join
end
To remove adjacent dupes only (ggorlen's answer is "aba"): a regular expression replacing adjacent repetitions is probably the go-to method.
def remove_adjacent_dupes(str)
str.gsub(/(.)\1+/, '')
end
Without regular expressions, we can use slice_when to cut the array when the character changes, then drop the groups that are too long. One might think a flatten would be required before join, but join doesn't care:
def remove_adjacent_dupes_without_regexp(str)
str.each_char.
slice_when { |prev, curr| prev != curr }.
select { |group| group.size == 1 }.
join
end
While amadan's and user's solution definitely solve the problem I felt like writing a solution closer to the OP's attempt:
def clean(string)
return string if string.length == 1
array = string.split('')
array.select.with_index do |value, index|
array[index - 1] != value && array[index + 1] != value
end.join
end
Here are a few examples:
puts clean("aaaaabccccdeeeeeefgggg")
#-> bdf
puts clean("m")
#-> m
puts clean("ttyyzx")
#-> zx
puts clean("aab")
#-> b
The method makes use of the fact that the characters are sorted and in case there are duplicates, they are either before or after the character that's being checked by the select method. The method is slower than the solutions posted above, but as OP mentioned he does not yet work with hashes yet I though this might be useful.
If speed is not an issue,
require 'set'
...
Set.new(("xxyz").split("")).to_a.join # => xyz
Making it a Set removes duplicates.
The OP does not want to remove duplicates and keep just a single copy, but remove all characters completely from occurring more than once. So here is a new approach, again compact, but not fast:
"xxyz".split('').sort.join.gsub(/(.)\1+/,'')
The idea is to sort the the letters; hence, identical letters will be joined together. The regexp /(.)\1+/ describes a repetition of a letter.

Swap adjacent elements in array

I am trying to build a method in Ruby that will take in a string that has been split into an array of letters and then iterate through the array, swapping the element at index n with that at index n+1. The method will then join the new array into a string and push it to another array.
Here is an example of what I am looking to do:
string = "teh"
some_method(string)
some ruby magic here
array << new_string
end
Expected output:
["eth", "the"]
This is for a spell checker program I am writing for school. The method will check if letters in a misspelled word are swapped by checking to see if the output array elements are in the dictionary. If they are, it will return the word with that is most likely the correct word. I haven't had any luck finding articles or documentation on how to build such a method in ruby or on an existing method to do this. I've been tinkering with building this method for awhile now but my code isn't behaving anything like what I need. Thanks in advance!
As #Sergio advised, you want to use parallel assignment for this:
def reverse_em(str)
(0...str.size-1).map do |i|
s = str.dup
s[i], s[i+1] = s[i+1], s[i]
s
end
end
candidates = reverse_em "alogrithm"
#=> ["laogrithm", "aolgrithm", "algorithm", "alorgithm",
# "alogirthm", "alogrtihm", "alogrihtm", "alogritmh"]
dictionary_check(candidates)
#=> algorithm
# al·go·rithm
# noun \ˈal-gə-ˌri-thəm\
# a set of steps that are followed in order to solve a
# mathematical problem or to complete a computer process
Without splitting it into arrays then joining to new arrays (because that doesn't seem necessary):
def some_method(string)
swapped_strings = []
(0...string.size-1).each do |i|
temp_string = string.dup
temp_string[i], temp_string[i+1] = temp_string[i+1], temp_string[i]
swapped_strings << temp_string
end
swapped_strings
end

Ruby. Range:Each change value

I'm trying to write a siple script, that calculates fibonacci numbers in a loop:
def fib(v)
return v if v < 2
(fib(v-2) + fib(v-1))
end
[0..15].each do |i|
puts "#{fib(i-1)} "
end
But this code fails because i-1 cannot be executed, as i has type Range. What should I do with it? I know, there are many other ways to calculate fibonacci numbers, but I need this code to work, not to rewrite it.
Issue: [] is the syntax for creating an array. So [0..15] creates an array with one element. That one element is the Range, 0..15. Range itself is an enumerable, so you can:
(0..15).each do |i|
puts fibonacci(i - 1)
end
As a side note, using interpolating strings in unnecessary when you have just 1 element to print.

ruby string array iteration. Array of arrays

I have a ruby problem
Here's what i'm trying to do
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.each do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
#sum2
I know i have over complicated this
Basically the input is a string of letters and values like "14556 this word 398"
I am trying to sum the numbers in each value, seperated by the whitespace like (" ")
When i use the def iterate1 method the block calls the counter1 method just fine, but i can only get the value for the last word or value in the string.
In this case that's 398, which when summed would be 27.
If i include a break i get the first value, which would be 21.
I'm looking to output an array with all of the summed values
Any help would be greatly appreciated
I think you're after:
"10 d 20 c".scan(/\b\d+\b/).map(&:to_i).inject(:+) # Returns 30
scan(/\b\d+\b/) will extract all numbers that are made up of digits only in an array, map(&:to_i) will convert them to integers and I guess you already know what inject(:+) will do.
I'm not sure if I understand what you're after correctly, though, so it might help if you provide the answer you expect to this input.
EDIT:
If you want to sum the digits in each number, you can do it with:
"12 d 34 c".scan(/\b\d+\b/).map { |x| x.chars.map(&:to_i).inject(:+) }
x.chars will return an enumerator for the digits, map(&:to_i) will convert them to integers and inject(:+) will sum them.
The simplest answer is to use map instead of each because the former collects the results and returns an array. e.g:
def iterate1 #define method in given class
#var3 = #var2.split(" ") #split string to array
#var4 = #var3
#var4.map do |i| #for each array item do i
ra = []
i.each_char {|d| ra << counter1(d)} # for each char in i, apply def counter1
#sum = ra.inject(:+)
#sum2 = #sum.inject(:+) #have to do the inject twice to get values
end
end
You could write it a lot cleaner though and I think Stefan was a big help. You could solve the issue with a little modification of his code
# when you call iterate, you should pass in the value
# even if you have an instance variable available (e.g. #var2)
def iterate(thing)
thing.scan(/\b\d+\b/).map do |x|
x.chars.map{|d| counter1(d)}.inject(:+)
end
end
The above assumes that the counter1 method returns back the value as an integer

Storing output into a variable to be used in an array

A snippet of my code below flips a coin and outputs a result of 10 total heads or tails.
(e.g. Heads Tails Heads Tails...)
I'd like to store this into a variable where I can put it into an array and use its strings.
%w[act] only outputs the string "act". How can I get that line of code to output my array of strings from the line act = coin.flip?
Updated and added full code
class Coin
def flip
flip = 1 + rand(2)
if flip == 2
then puts "Heads"
else
puts "Tails"
end
end
end
array = []
10.times do
coin = Coin.new
array << coin.flip
end
puts array
This:
10.times do
coin = Coin.new
act = coin.flip
end
doesn't produce an array. It simply creates ten coin flips and throws them all away, the result of that expression is, in fact, 10. If you want an array, you'll need to build one.
You could take Douglas's approach or try something a bit more idiomatic.
The Integer#times method returns an enumerator so you can use any of the Enumerable methods on it rather than directly handing it a block. In particular, you could use collect to build an array in one nice short piece of code:
a = 10.times.collect { Coin.new.flip }
That gives you 10 flips in the Array a and then you can puts a or puts a.join(', ') or whatever you want.
The %w[] won't work because that's for generating an Array of whitespace separated words:
%w[] Non-interpolated Array of words, separated by whitespace
So %w[a b c] is just a nicer way of saying ['a', 'b', 'c'] and the words within %w[] are treated as single quoted strings rather than variables or method calls to be evaluated.
Seems that there is some editing going on. You'll also want to modify your flip method to return the flip rather than print it:
def flip
flip = 1 + rand(2)
if flip == 2
"Heads"
else
"Tails"
end
end
Then you'll get your Heads and Rails in the array.
Put the act results into an array.
arr = []
10.times do
coin = Coin.new
arr << coin.flip
end
p arr # => [...]

Resources