Related
I was doing this question on codewars: "Given an array, find the int that appears an odd number of times. There will always be only one integer that appears an odd number of times."
Code:
def find_it(seq)
int = []
for a in seq do
count = 0
for b in seq do
if a == b
count += 1
end
end
if count % 2.0 != 0
int << b
end
end
puts int.uniq[0].to_i
end
It was tested against a couple inputs, but the answers were wrong for these two arrays:
find_it([1,1,2,-2,5,2,4,4,-1,-2,5]) - returns 5 instead of -1
find_it([1,1,1,1,1,1,10,1,1,1,1]) - returns 1 instead of 10
What went wrong with my code?
if count % 2.0 != 0
int << b
end
The problem you have here is that your pushing b instead of a into the integer array, so what's happening is that instead of the value that you counted being pushed in, your pushing in the last value of b which is the last value element in the array regardless as long as the condition that the counter is an odd number, although b and counter have nothing to do with each other. so to fix it you replace b with a so that it pushes in the value you are testing comparing with the other elements in the second loop
fix:
if count % 2.0 != 0
int << a
end
a similar yet simpler code that does a similar job except in a shorter and more understandable way is:
def find_it(seq)
numberWithOddCount = 0
seq.each do |currentElement|
counter = 0
seq.each { |elementToCompare| counter += 1 if currentElement == elementToCompare}
numberWithOddCount = currentElement if counter % 2 != 0
end
numberWithOddCount
end
Just added a few tid-bits that you could also utilize to shorten and simplify code.
Happy Coding!
Note:
You could utilize built in ruby methods in creative ways to make the code do what you want in very few lines (or even one line) such as what #iGian did in the questions comments, but if your still new to ruby then its best to utilize those methods one by one when learning them otherwise you'll be confused. But if your willing to take the time now to learn them, I suggest you take his code and separate each method execution into its own line and output what each method had done to know what's doing what. and practice using each separately.
#aimen_alt is right about your mistake
but let's decompose your problem.
First, you need to calculate the appearances of each number.
Second, you need to find the one with the odd count of the appearances.
Accordingly to the problem, there is only one such number, so you can return it right away.
You can go your way and do it in O(N^2) complexity by scanning your sequence for each item in the sequence (so N items in the sequence multiply by the size of the sequence N = N*N). You can do it linearly* by constructing a Hash and than you'll be able to get the key with odd value:
def find_it(seq)
numbers = {}
seq.each do |item|
numbers[item] = numbers[item].to_i + 1
end
numbers.select{ |k,v| v.odd? }.first.first
end
to be more idiomatic you can use group_by to group the numbers themselves:
seq = [1, 2, 6, 1, 2]
seq.group_by{ |item| item }
#=> {1=>[1, 1], 2=>[2, 2], 6=>[6]}
You can see that each value is an Array, and you just need to get one with the odd amount of items:
seq = [1, 2, 6, 1, 2]
seq.group_by{ |item| item }.select{ |k, v| v.size.odd? }
#=> {6=>[6]}
And the last thing you would like to do is to get the value of the key:
seq.group_by{ |item| item }.select{ |k, v| v.size.odd? }.keys.first
So, the final solution would be
def find_it(seq)
seq.group_by{ |item| item }
.select{ |k, v| v.size.odd? }
.keys
.first
end
as #pascalbetz mentioned:
def find_it(seq)
seq.group_by{ |item| item }
.find{ |k, v| v.size.odd? }
.first
end
def find_it(seq)
seq.group_by{|x| x}.select{|k, v| (v.count % 2.0 !=0)}.first[0]
end
The above code will take a sequence in an array. Here we are grouping by elements:
For example:
[1,1,2,-2,5,2,4,4,-1,-2,5].group_by{|x| x}
# => {1=>[1, 1], 2=>[2, 2], -2=>[-2, -2], 5=>[5, 5], 4=>[4, 4], -1=>[-1]}
after getting the above results, we are finding the whose elements count not odd with the select condition.
ex:
[1,1,2,-2,5,2,4,4,-1,-2,5].group_by{|x| x}.select{|k, v| (v.count % 2.0 !=0)}
we will get the results as {-1=>[-1]}
we are taking the key as result element.
What about this one
def find_it(seq)
seq.reduce(:^)
end
^ -> this symbol is bitwise XOR.
reduce function is taking each value and doing whatever work assigned inside. In this case, it's taking each element and doing an XOR operation. the first element is doing XOR with zero and the next element doing XOR with the previous result and so on.
In this way, we found the odd element.
How XOR operation work
0 ^ 2 = 2
4 ^ 4 = 0
If you want to know more about XOR. kindly refer to this.
Thank you for all the detailed answers, I'm going over everyone's answers now. I'm new to Ruby, and I'm still in the process of learning the methods/rules of using them/Big O notation, so I much appreciated everyone's input. Codewar listed some top ranked solutions. This seems to be the fastest so far:
def find_it(seq)
seq.detect { |n| seq.count(n).odd? }
end
I was trying to create a Table of Contents in the command line. Each element in the array is a string variable.
arr = [chap1, chap1_page, chap2, chap2_page, chap3, chap3_page]
x = 0
until x == arr.length
if ((arr[x] != 0 ))
puts arr[x].ljust(line/2) + arr[x += 1].rjust(line/2)
end
x += 1
end
I was wondering if someone could explain the second half of the puts statement. I'm not sure why arr[x+=1] works but arr[x+1] does not. As far as I know, they are the same, are they not?
When dealing with Enumerables like arrays and hashes, it's useful to search the documentation to see if there's something there that will make your code higher level and more expressive. In this case, you can use each_cons to give you the pairs so you don't need to use array indexes at all:
2.3.0 :004 > [1,2,3,4].each_cons(2).to_a
=> [[1, 2], [2, 3], [3, 4]]
Also, rather than using if statements, it's better IMO to use select and reject.
Also, intermediate local variables can make your code more readable.
Using these ideas, your code could look something like this:
array = [chap1, chap1_page, chap2, chap2_page, chap3, chap3_page]
width = line / 2
array.each_cons(2).reject { |x,y| x == 0 }.each do |left, right|
puts left.ljust(width) + right.ljust(width)
end
(I haven't tested this code, but it shows the general idea.)
You could break down those enumerable calls and assign intermediate values to local variables if that makes it clearer for you:
array = [chap1, chap1_page, chap2, chap2_page, chap3, chap3_page]
width = line / 2
pairs = array.each_cons(2)
nonzero_pairs = pairs.reject { |x,y| x == 0 }
nonzero_pairs.each do |left, right|
puts left.ljust(width) + right.rjust(width)
end
x + 1 returns that value and has no side effect (does not change the reference of x). x += 1 reassigns x and returns that value.
I came across a website called Project Euler and everything was going well until I hit the 3rd problem - The Largest Prime Factor. I don't want to use recursion to solve it. I saw solutions online where they use Math.sqrt and I don't want to use that either. Stubborn, I know.
I'd like to solve it with just loops and if statements. I assumed the input is an odd number. Here is my code. The output keeps coming out as [3] if num = 99 and I can't figure out why. I tried putting a puts statement everywhere to see what was being outputted at each step. One issue I realized was that that the array#p was not resetting after each loop. I tried array.clear but that wasn't much help. Could someone point me in the right direction? Is there some fundamental aspect about arrays, loops, and if-statements that I'm not getting?
def prime(num)
arr = []
p = []
not_p = []
# first I find all the numbers that num is divisible by
for i in (2..num/2)
if num % i == 0
arr << i
end
end # this should output [3, 9, 11, 33]
arr.each do |x| # I loop through each element in the above array
for i in (2..(x/2)) # I divide each element - x - by 2 because it cannot be divisble by anything greater than its half
if x % i == 0 # if x is divisble by i
not_p << i # I push the i into array#not_p
end # keep looping until i reaches x/2
end
if not_p.length == 0 # if there are no values in array#not_p, then I know x is a prime factor
p << x # so I push x into array#p
end
end
return p[-1] # returns the last element of the array, which is the largest
end
puts prime(99)
I'm not going to give you the full answer, as that would defeat the object of the practice with Project Euler.
However, you're almost on the right track with sorting out your problem. You don't want to look at the array p not being emptied, that should be collecting your primes. You do want to look at not_p though, since that is the array of divisors of each of your factors.
I hope this helps. Let me know if I can help any more.
Ah ok! Thanks for the suggestion philnash! In fact, I knew about that problem and tried to clear the array with Array.clear but that did not work. Instead, I just moved not_p = [] below the iteration arr.each do |x| and it worked! It makes sense because the not_p resets to [] when it moves on to the next element. Thanks so much for your help and for not providing the answer first! Here is my final, working solution =D
def prime(num)
arr = []
p = []
for i in (2..num / 2)
if num % i == 0
arr << i
end
end # this should output [3, 9, 11, 33]
arr.each do |x|
not_p = []
for i in (2..(x / 2))
if x % i == 0
not_p << i
end
end
if not_p.length == 0
p << x
end
end
return p[-1]
end
puts prime(99) # => 29
I want to iterate through an array, each element of which is an array of two integers (e.g. `[3,5]'); for each of these elements, I want to calculate the sum of the two integers, exiting the loop when any of these sums exceeds a certain arbitrary value. The source array is quite large, and I will likely find the desired value near the beginning, so looping through all of the unneeded elements is not a good option.
I have written three loops to do this, all of which produce the desired result. My question is: which is more idiomatic Ruby? Or--better yet--is there a better way? I try not to use non-local loop variables in, but break statements look kind of hackish to my (admittedly novice) eye.
# Loop A
pairs.each do |pair|
pair_sum = pair.inject(:+)
arr1 << pair_sum
break if pair_sum > arr2.max
end
#Loop B - (just A condensed)
pairs.each { |pair| arr1.last <= arr2.max ? arr1 << pair.inject(:+) : break }
#Loop C
i = 0
pair_sum = 0
begin
pair_sum = pairs[i].inject(:+)
arr1 << pair_sum
i += 1
end until pair_sum > arr2.max
A similar question was asked at escaping the .each { } iteration early in Ruby, but the responses were essentially that, while using .each or .each_with_index and exiting with break when the target index was reached would work, .take(num_elements).each is more idiomatic. In my situation, however, I don't know in advance how many elements I'll have to iterate through, presenting me with what appears to be a boundary case.
This is from a project Euler-type problem I've already solved, btw. Just wondering about the community-preferred syntax. Thanks in advance for your valuable time.
take and drop have a variant take_while and drop_while where instead of providing a fixed number of elements you provide a block. Ruby will accumulate values from the receiver (in the case of take_while) as long as the block returns true. Your code could be rewritten as
array.take_while {|pair| pair.sum < foo}.map(&:sum)
This does mean that you calculate the sum of some of these pairs twice.
In Ruby 2.0 there's Enumerable#lazy which returns a lazy enumerator:
sums = pairs.lazy.map { |a, b| a + b }.take_while { |pair_sum| pair_sum < some_max_value }.force
This avoids calculating the sums twice.
[[1, 2], [3, 4], [5, 6]].find{|x, y| x + y > 6}
# => [3, 4]
[[1, 2], [3, 4], [5, 6]].find{|x, y| x + y > 6}.inject(:+)
#=> 7
Say I have an array that looks like:
a = [cat, dog, cat, mouse, rat, dog, cat]
How do I cycle through that, and do something with duplicates - e.g. say delete them?
In other words, if I did a.each do |i|, how do I evaluate a[0], against a[1], a[2], a[3]...and then when I find the one I want, say a[2] in this case has the first duplicate, I then push it to a stack or remove it or something.
I know how to evaluate keys, versus values...but how do I evaluate values against each other within the same array?
Thanks.
You can create a hash to store number of times any element is repeated. Thus iterating over array just once.
h = Hash.new(0)
['a','b','b','c'].each{ |e| h[e] += 1 }
Should result
{"a"=>1, "b"=>2, "c"=>1}
This works efficiently and is rather simple:
require 'set'
visited = Set.new
array.each do |element|
if visited.include?(element)
# duplicated item
else
# first appearance
visited << element
end
end
Try this:
class Array
def find_dups
uniq.map {|v| (self - [v]).size < (self.size - 1) ? v : nil}.compact
end
end
a = ['cat', 'dog', 'cat', 'mouse', 'rat', 'dog', 'cat']
print a - a.find_dups # Removes duplicates
find_dups will return elements that have duplicates
Try this:
array.inject({}){|h, e| h[e] = h[e].to_i + 1; h}
Use
a.uniq! to remove duplicates .
also checkout the ruby-doc.org where you can find more info on ruby's class methods .
A simple solution is to run a double loop:
a.each_with_index do |a1, idx1|
a.each_with_index do |a2, idx2|
next if idx1 >= idx2 # Don't compare element to itself
# and don't repeat comparisons already made
# do something with a pair of elements (a1, a2)
end
end
If you just want to eliminate duplicates, there's a method: Array#uniq.
This will print all the duplicates in an array:
array.inject(Hash.new(0)) { |hash,val|
hash[val] += 1;
hash
}.each_pair { |val,count|
puts "#{val} -> #{count}" if count > 1
}
The best way to do it is to compare it with a unique version of itself. If its the same then it has no duplicates, if not then duplicates exist.
unique_array = original_array.uniq
get a unique version of your array
if original_array == unique_array then return true else return false
compare it to your original array.
Simple!
If you just want to get rid of duplicates, the easiest thing to do is take the array and do array&array. Use the & operator.
If you want to know what those repeats are, just compare array to array&array.
If array is sortable, then something like below will return only the duplicates.
array.sort.each_cons(2).select {|p| p[0] == p[1] }.map &:first
Sorts the array, then maps it to consecutive pairs of elements, selects pairs which are same, maps to elements.