Ruby Coding

Ruby Coding Problems Interview Questions and Answers

Practice commonly asked Ruby coding problems with detailed solutions. Covers arrays, strings, hashes, and algorithms with time complexity analysis.

📋 Jump to Question

Ruby Coding Interview Questions - Beginner to Intermediate

Table of Contents

  1. Array Flattening
  2. Hash Flattening
  3. Nil Removal
  4. Array Reversal
  5. Finding Maximum Elements
  6. Finding Second Largest
  7. Duplicate Removal
  8. Array Merging
  9. Array Rotation
  10. Finding Pairs
  11. Moving Zeroes
  12. Array Intersection
  13. Frequency Counting
  14. Additional Array Problems
  15. String Problems
  16. Hash Problems
  17. Number Problems

1. Array Flattening

Q1: Flatten a nested array without using the built-in flatten method

Explanation: This recursive solution traverses the array and flattens any nested arrays it encounters. The base case is when an element is not an array - we simply add it to the result. When we encounter an array, we recursively flatten it and concatenate the results.

def custom_flatten(array)
  result = []
  array.each do |element|
    if element.is_a?(Array)
      # Recursively flatten the nested array and add all its elements
      result += custom_flatten(element)
    else
      # Add non-array elements directly
      result << element
    end
  end
  result
end

# Example usage:
nested_array = [1, [2, [3, 4], 5], 6]
puts custom_flatten(nested_array).inspect #=> [1, 2, 3, 4, 5, 6]

# Test with different nesting levels
puts custom_flatten([1, [2, 3], [[4, 5], 6]]).inspect #=> [1, 2, 3, 4, 5, 6]
puts custom_flatten([]).inspect #=> []
puts custom_flatten([1, 2, 3]).inspect #=> [1, 2, 3]

Time Complexity: O(n) where n is total number of elements Space Complexity: O(n) for the result array

Q2: Flatten an array iteratively (without recursion)

Explanation: An iterative approach using a stack. This avoids recursion depth limitations for very deeply nested arrays.

def iterative_flatten(array)
  result = []
  stack = array.dup
  
  while !stack.empty?
    element = stack.shift
    if element.is_a?(Array)
      # Prepend array elements to stack (to maintain order)
      stack = element + stack
    else
      result << element
    end
  end
  result
end

puts iterative_flatten([1, [2, [3, 4], 5], 6]).inspect #=> [1, 2, 3, 4, 5, 6]

2. Hash Flattening

Q3: Flatten a nested hash with dot notation for keys

Explanation: This recursive solution traverses a nested hash and creates a flat hash where nested keys are represented with dot notation (e.g., "parent.child.grandchild").

def flatten_hash(hash, parent_key = nil, result = {})
  hash.each do |key, value|
    current_key = parent_key ? "#{parent_key}.#{key}" : key.to_s
    
    if value.is_a?(Hash)
      # Recursively flatten nested hash
      flatten_hash(value, current_key, result)
    else
      # Store non-hash values with their flattened key
      result[current_key] = value
    end
  end
  result
end

# Example usage:
nested_hash = {a: 1, b: {c: 2, d: {e: 3}}}
puts flatten_hash(nested_hash).inspect 
#=> {"a"=>1, "b.c"=>2, "b.d.e"=>3}

# More complex example
complex_hash = {
  user: {
    name: "John",
    address: {
      city: "NYC",
      zip: 10001
    },
    contacts: {
      email: "john@example.com",
      phone: "123-456-7890"
    }
  },
  meta: { version: 1 }
}
puts flatten_hash(complex_hash).inspect
#=> {"user.name"=>"John", "user.address.city"=>"NYC", "user.address.zip"=>10001,
#    "user.contacts.email"=>"john@example.com", "user.contacts.phone"=>"123-456-7890",
#    "meta.version"=>1}

Time Complexity: O(n) where n is total number of key-value pairs Space Complexity: O(n) for the result hash

Q4: Flatten hash with custom delimiter

Explanation: Enhanced version that allows custom delimiter between nested keys.

def flatten_hash_with_delimiter(hash, parent_key = nil, delimiter = '.', result = {})
  hash.each do |key, value|
    current_key = parent_key ? "#{parent_key}#{delimiter}#{key}" : key.to_s
    
    if value.is_a?(Hash)
      flatten_hash_with_delimiter(value, current_key, delimiter, result)
    else
      result[current_key] = value
    end
  end
  result
end

puts flatten_hash_with_delimiter(nested_hash, nil, '/').inspect
#=> {"a"=>1, "b/c"=>2, "b/d/e"=>3}

3. Nil Removal

Q5: Remove nil values from an array without using compact

Explanation: Several approaches to filter out nil values from an array.

# Method 1: Using each_with_object
def remove_nils(array)
  array.each_with_object([]) do |element, result|
    result << element unless element.nil?
  end
end

# Method 2: Using reject (elegant, but uses built-in)
def remove_nils_reject(array)
  array.reject { |x| x.nil? }
end

# Method 3: Manual iteration
def remove_nils_manual(array)
  result = []
  array.each { |element| result << element if element }
  result
end

# Method 4: Using select with non-nil condition
def remove_nils_select(array)
  array.select { |x| !x.nil? }
end

# Example usage:
array_with_nils = [1, nil, 2, nil, 3, nil, nil, 4]
puts remove_nils(array_with_nils).inspect #=> [1, 2, 3, 4]

# Compact version (for comparison)
puts array_with_nils.compact.inspect #=> [1, 2, 3, 4]

Time Complexity: O(n) Space Complexity: O(n)

Q6: Remove nil and false values

Explanation: Sometimes we want to remove both nil and false values.

def remove_nil_and_false(array)
  array.select { |element| element != nil && element != false }
end

# Or more concisely
def remove_falsy(array)
  array.select { |element| element }
end

mixed_array = [1, nil, false, 2, true, 3, nil]
puts remove_falsy(mixed_array).inspect #=> [1, 2, true, 3]

4. Array Reversal

Q7: Reverse an array without using built-in reverse

Explanation: Multiple approaches to reverse an array manually.

# Method 1: Using unshift
def reverse_array_manual(arr)
  reversed = []
  arr.each { |element| reversed.unshift(element) }
  reversed
end

# Method 2: Using indices
def reverse_array_indices(arr)
  reversed = []
  (arr.length - 1).downto(0) do |i|
    reversed << arr[i]
  end
  reversed
end

# Method 3: In-place reversal
def reverse_in_place!(arr)
  left = 0
  right = arr.length - 1
  while left < right
    arr[left], arr[right] = arr[right], arr[left]
    left += 1
    right -= 1
  end
  arr
end

# Method 4: Using inject
def reverse_inject(arr)
  arr.inject([]) { |reversed, element| reversed.unshift(element) }
end

# Example usage:
original = [1, 2, 3, 4, 5]
puts reverse_array_manual(original).inspect #=> [5, 4, 3, 2, 1]
puts reverse_array_indices(original).inspect #=> [5, 4, 3, 2, 1]

# In-place modifies original
arr = [1, 2, 3, 4, 5]
reverse_in_place!(arr)
puts arr.inspect #=> [5, 4, 3, 2, 1]

Time Complexity: O(n) Space Complexity: O(n) for new array methods, O(1) for in-place


5. Finding Maximum Elements

Q8: Find the maximum element without using max

Explanation: Manual approaches to find the maximum value in an array.

# Method 1: Simple iteration
def find_max_manual(arr)
  return nil if arr.empty?
  
  max = arr.first
  arr.each { |num| max = num if num > max }
  max
end

# Method 2: Using reduce
def find_max_reduce(arr)
  arr.reduce { |max, num| num > max ? num : max }
end

# Method 3: Using sort (inefficient but possible)
def find_max_sort(arr)
  arr.sort.last
end

# Example usage:
numbers = [3, 7, 2, 9, 1, 5]
puts find_max_manual(numbers) #=> 9
puts find_max_reduce(numbers) #=> 9
puts find_max_sort(numbers) #=> 9

# Edge cases
puts find_max_manual([]).inspect #=> nil
puts find_max_manual([5]).inspect #=> 5

Time Complexity: O(n) Space Complexity: O(1)

Q9: Find maximum and minimum simultaneously

Explanation: Finding both max and min in a single pass.

def find_min_max(arr)
  return [nil, nil] if arr.empty?
  
  min = max = arr.first
  arr.each do |num|
    min = num if num < min
    max = num if num > max
  end
  [min, max]
end

numbers = [3, 7, 2, 9, 1, 5]
min, max = find_min_max(numbers)
puts "Min: #{min}, Max: #{max}" #=> Min: 1, Max: 9

6. Finding Second Largest

Q10: Find the second largest number without sorting

Explanation: Multiple approaches to find the second largest element.

# Method 1: Using uniq and sort (simple)
def second_largest_sort(arr)
  return nil if arr.size < 2
  arr.uniq.sort[-2]
end

# Method 2: Single pass (more efficient)
def second_largest_manual(arr)
  return nil if arr.size < 2
  
  largest = -Float::INFINITY
  second = -Float::INFINITY
  
  arr.each do |num|
    if num > largest
      second = largest
      largest = num
    elsif num > second && num < largest
      second = num
    end
  end
  
  second == -Float::INFINITY ? nil : second
end

# Method 3: With negative numbers handling
def second_largest_with_negatives(arr)
  return nil if arr.size < 2
  
  arr = arr.uniq
  return nil if arr.size < 2
  
  arr.sort[-2]
end

# Example usage:
numbers = [3, 7, 2, 9, 1, 5]
puts second_largest_manual(numbers) #=> 7
puts second_largest_sort(numbers) #=> 7

# With duplicates
numbers_with_dupes = [5, 5, 5, 3, 3, 8]
puts second_largest_manual(numbers_with_dupes) #=> 5
puts second_largest_sort(numbers_with_dupes) #=> 5

Time Complexity: O(n) for manual, O(n log n) for sort Space Complexity: O(1) for manual, O(n) for sort with uniq

Q11: Find nth largest element

Explanation: Generalized solution for finding the nth largest element.

def nth_largest(arr, n)
  return nil if arr.size < n || n < 1
  
  arr.uniq.sort[-n]
end

puts nth_largest([3, 7, 2, 9, 1, 5], 3) #=> 5 (3rd largest)
puts nth_largest([3, 7, 2, 9, 1, 5], 1) #=> 9 (1st largest)
puts nth_largest([3, 7, 2, 9, 1, 5], 6) #=> 1 (6th largest)

7. Duplicate Removal

Q12: Remove duplicates without using uniq

Explanation: Manual approaches to remove duplicate elements.

# Method 1: Using a hash (most efficient)
def remove_duplicates_hash(arr)
  result = []
  seen = {}
  
  arr.each do |element|
    unless seen[element]
      result << element
      seen[element] = true
    end
  end
  result
end

# Method 2: Using array inclusion (slower for large arrays)
def remove_duplicates_include(arr)
  result = []
  arr.each do |element|
    result << element unless result.include?(element)
  end
  result
end

# Method 3: Using Set
require 'set'
def remove_duplicates_set(arr)
  arr.to_set.to_a
end

# Example usage:
duplicates = [1, 2, 2, 3, 4, 4, 4, 5]
puts remove_duplicates_hash(duplicates).inspect #=> [1, 2, 3, 4, 5]
puts remove_duplicates_include(duplicates).inspect #=> [1, 2, 3, 4, 5]

# Maintain order of first occurrence
mixed = [3, 1, 2, 1, 3, 4]
puts remove_duplicates_hash(mixed).inspect #=> [3, 1, 2, 4]

Time Complexity: O(n) for hash method, O(n²) for include method Space Complexity: O(n)

Q13: Remove duplicates while keeping count

Explanation: Not just removing duplicates but tracking how many times each appeared.

def remove_duplicates_with_count(arr)
  counts = Hash.new(0)
  arr.each { |element| counts[element] += 1 }
  
  {
    unique_elements: counts.keys,
    element_counts: counts,
    duplicates: counts.select { |_, count| count > 1 }.keys
  }
end

result = remove_duplicates_with_count([1, 2, 2, 3, 3, 3, 4])
puts result[:unique_elements].inspect #=> [1, 2, 3, 4]
puts result[:element_counts].inspect #=> {1=>1, 2=>2, 3=>3, 4=>1}
puts result[:duplicates].inspect #=> [2, 3]

8. Array Merging

Q14: Merge two sorted arrays

Explanation: Multiple approaches to merge sorted arrays.

# Method 1: Simple concatenation and sort (not optimal for large arrays)
def merge_sorted_simple(arr1, arr2)
  (arr1 + arr2).sort
end

# Method 2: Manual merge (O(n+m) - optimal for sorted inputs)
def merge_sorted_manual(arr1, arr2)
  merged = []
  i = j = 0
  
  while i < arr1.length && j < arr2.length
    if arr1[i] <= arr2[j]
      merged << arr1[i]
      i += 1
    else
      merged << arr2[j]
      j += 1
    end
  end
  
  # Add remaining elements
  merged.concat(arr1[i..-1]) if i < arr1.length
  merged.concat(arr2[j..-1]) if j < arr2.length
  
  merged
end

# Method 3: Using built-in merge (if we could use Ruby's features)
def merge_sorted_builtin(arr1, arr2)
  arr1.merge(arr2) # Only works with hashes, not arrays
end

# Example usage:
arr1 = [1, 3, 5, 7]
arr2 = [2, 4, 6, 8]
puts merge_sorted_manual(arr1, arr2).inspect #=> [1, 2, 3, 4, 5, 6, 7, 8]

# With duplicates
arr3 = [1, 2, 3]
arr4 = [2, 3, 4]
puts merge_sorted_manual(arr3, arr4).inspect #=> [1, 2, 2, 3, 3, 4]

Time Complexity: O(n+m) for manual merge Space Complexity: O(n+m)

Q15: Merge multiple sorted arrays

Explanation: Extending to merge any number of sorted arrays.

def merge_multiple_sorted(arrays)
  # Flatten and sort is simple but not optimal
  arrays.flatten.sort
  
  # More efficient would be to use a priority queue
  # This is a simplified version
  result = []
  indices = [0] * arrays.length
  
  loop do
    # Find minimum among current elements
    min_val = nil
    min_idx = nil
    
    arrays.each_with_index do |arr, i|
      next if indices[i] >= arr.length
      
      current = arr[indices[i]]
      if min_val.nil? || current < min_val
        min_val = current
        min_idx = i
      end
    end
    
    break if min_idx.nil?
    
    result << min_val
    indices[min_idx] += 1
  end
  
  result
end

arrays = [[1, 4, 7], [2, 5, 8], [3, 6, 9]]
puts merge_multiple_sorted(arrays).inspect #=> [1, 2, 3, 4, 5, 6, 7, 8, 9]

9. Array Rotation

Q16: Rotate array left by k positions

Explanation: Multiple approaches to rotate an array.

# Method 1: Using built-in rotate
def rotate_left(arr, k)
  arr.rotate(k)
end

# Method 2: Manual rotation
def rotate_left_manual(arr, k)
  k = k % arr.length  # Handle k > array length
  arr[k..-1] + arr[0...k]
end

# Method 3: In-place rotation
def rotate_left_in_place!(arr, k)
  k = k % arr.length
  return arr if k == 0
  
  # Reverse entire array
  reverse!(arr, 0, arr.length - 1)
  # Reverse first part
  reverse!(arr, 0, arr.length - k - 1)
  # Reverse second part
  reverse!(arr, arr.length - k, arr.length - 1)
  arr
end

def reverse!(arr, start, finish)
  while start < finish
    arr[start], arr[finish] = arr[finish], arr[start]
    start += 1
    finish -= 1
  end
end

# Example usage:
arr = [1, 2, 3, 4, 5]
puts rotate_left_manual(arr, 2).inspect #=> [3, 4, 5, 1, 2]
puts rotate_left_manual(arr, 7).inspect #=> [3, 4, 5, 1, 2] (7 % 5 = 2)

# In-place rotation
arr2 = [1, 2, 3, 4, 5]
rotate_left_in_place!(arr2, 2)
puts arr2.inspect #=> [3, 4, 5, 1, 2]

Time Complexity: O(n) Space Complexity: O(n) for new array, O(1) for in-place

Q17: Rotate array right by k positions

Explanation: Right rotation is the complement of left rotation.

def rotate_right(arr, k)
  k = k % arr.length
  arr[-k..-1] + arr[0...-k]
end

arr = [1, 2, 3, 4, 5]
puts rotate_right(arr, 2).inspect #=> [4, 5, 1, 2, 3]

# Using left rotation
def rotate_right_via_left(arr, k)
  k = k % arr.length
  rotate_left_manual(arr, arr.length - k)
end

puts rotate_right_via_left(arr, 2).inspect #=> [4, 5, 1, 2, 3]

10. Finding Pairs

Q18: Find all pairs that sum to a target

Explanation: Multiple approaches to find pairs with a given sum.

# Method 1: Using combination (simple but O(n²))
def find_pairs_combination(arr, target)
  arr.combination(2).select { |a, b| a + b == target }
end

# Method 2: Using hash set (more efficient)
def find_pairs_hash(arr, target)
  pairs = []
  seen = {}
  
  arr.each do |num|
    complement = target - num
    if seen[complement]
      pairs << [complement, num]
    end
    seen[num] = true
  end
  
  pairs
end

# Method 3: Find all unique pairs
def find_unique_pairs(arr, target)
  arr.combination(2)
     .select { |a, b| a + b == target }
     .map(&:sort)
     .uniq
end

# Example usage:
numbers = [1, 2, 3, 4, 5]
puts find_pairs_hash(numbers, 6).inspect #=> [[1, 5], [2, 4]]

# With duplicates
numbers_with_dupes = [1, 1, 2, 3, 3, 4]
puts find_unique_pairs(numbers_with_dupes, 5).inspect #=> [[1, 4], [2, 3]]

Time Complexity: O(n) with hash, O(n²) with combination Space Complexity: O(n)

Q19: Find pairs with difference k

Explanation: Finding pairs with a specific difference.

def pairs_with_difference(arr, k)
  pairs = []
  seen = {}
  
  arr.each do |num|
    # Check for num + k
    if seen[num + k]
      pairs << [num, num + k]
    end
    # Check for num - k
    if seen[num - k]
      pairs << [num - k, num]
    end
    seen[num] = true
  end
  
  pairs.uniq
end

numbers = [1, 3, 5, 7, 9]
puts pairs_with_difference(numbers, 2).inspect #=> [[1, 3], [3, 5], [5, 7], [7, 9]]

11. Moving Zeroes

Q20: Move all zeroes to the end while maintaining order

Explanation: Multiple approaches to move zeroes to the end.

# Method 1: Partition approach
def move_zeroes(arr)
  non_zeroes = arr.reject { |x| x == 0 }
  zeroes = Array.new(arr.length - non_zeroes.length, 0)
  non_zeroes + zeroes
end

# Method 2: In-place movement
def move_zeroes_in_place!(arr)
  non_zero_index = 0
  
  # Move all non-zero elements forward
  arr.each_with_index do |num, i|
    if num != 0
      arr[non_zero_index] = num
      non_zero_index += 1
    end
  end
  
  # Fill remaining positions with zeros
  (non_zero_index...arr.length).each do |i|
    arr[i] = 0
  end
  
  arr
end

# Method 3: Using partition
def move_zeroes_partition(arr)
  arr.partition { |x| x != 0 }.flatten
end

# Example usage:
numbers = [1, 0, 3, 0, 5, 0, 7]
puts move_zeroes(numbers).inspect #=> [1, 3, 5, 7, 0, 0, 0]

# In-place modification
arr = [1, 0, 3, 0, 5]
move_zeroes_in_place!(arr)
puts arr.inspect #=> [1, 3, 5, 0, 0]

Time Complexity: O(n) Space Complexity: O(n) for new array, O(1) for in-place

Q21: Move zeroes to the beginning

Explanation: Variation where zeroes go to the front.

def move_zeroes_to_front(arr)
  zeroes = arr.select { |x| x == 0 }
  non_zeroes = arr.reject { |x| x == 0 }
  zeroes + non_zeroes
end

numbers = [1, 0, 3, 0, 5]
puts move_zeroes_to_front(numbers).inspect #=> [0, 0, 1, 3, 5]

12. Array Intersection

Q22: Find intersection of two arrays

Explanation: Multiple approaches to find common elements.

# Method 1: Using & operator (built-in)
def array_intersection(arr1, arr2)
  arr1 & arr2
end

# Method 2: Manual using hash
def intersection_manual(arr1, arr2)
  result = []
  seen = {}
  
  arr1.each { |element| seen[element] = true }
  arr2.each { |element| result << element if seen[element] }
  
  result.uniq
end

# Method 3: Preserve order from first array
def intersection_preserve_order(arr1, arr2)
  set2 = arr2.to_set
  arr1.select { |element| set2.include?(element) }.uniq
end

require 'set'
def intersection_with_set(arr1, arr2)
  set1 = arr1.to_set
  set2 = arr2.to_set
  (set1 & set2).to_a
end

# Example usage:
arr1 = [1, 2, 3, 4, 5]
arr2 = [4, 5, 6, 7, 8]
puts array_intersection(arr1, arr2).inspect #=> [4, 5]

Time Complexity: O(n+m) with hash Space Complexity: O(min(n,m))

Q23: Find union of two arrays

Explanation: Combining elements from both arrays without duplicates.

def array_union(arr1, arr2)
  (arr1 + arr2).uniq
end

def union_manual(arr1, arr2)
  result = []
  seen = {}
  
  (arr1 + arr2).each do |element|
    unless seen[element]
      result << element
      seen[element] = true
    end
  end
  
  result
end

arr1 = [1, 2, 3]
arr2 = [3, 4, 5]
puts array_union(arr1, arr2).inspect #=> [1, 2, 3, 4, 5]

13. Frequency Counting

Q24: Count frequency of each element

Explanation: Multiple approaches to count element frequencies.

# Method 1: Using tally (Ruby 2.7+)
def count_frequency_tally(arr)
  arr.tally
end

# Method 2: Manual using hash
def count_frequency_manual(arr)
  frequency = Hash.new(0)
  arr.each { |element| frequency[element] += 1 }
  frequency
end

# Method 3: With element ordering
def count_frequency_ordered(arr)
  arr.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
end

# Example usage:
elements = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
puts count_frequency_manual(elements).inspect 
#=> {1=>1, 2=>2, 3=>3, 4=>4}

# With strings
words = %w[a b a c a b]
puts count_frequency_manual(words).inspect #=> {"a"=>3, "b"=>2, "c"=>1}

Time Complexity: O(n) Space Complexity: O(n)

Q25: Find most frequent element

Explanation: Finding the element with highest frequency.

def most_frequent(arr)
  return nil if arr.empty?
  
  freq = arr.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
  max_freq = freq.values.max
  freq.select { |_, count| count == max_freq }.keys
end

def most_frequent_single(arr)
  freq = arr.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
  freq.max_by { |_, count| count }.first
end

numbers = [1, 2, 2, 3, 3, 3, 4]
puts most_frequent(numbers).inspect #=> [3]
puts most_frequent_single(numbers) #=> 3

# With tie
numbers_with_tie = [1, 1, 2, 2, 3]
puts most_frequent(numbers_with_tie).inspect #=> [1, 2]

14. Additional Array Problems

Q26: Check if array is palindrome

Explanation: Determine if an array reads the same forwards and backwards.

def palindrome?(arr)
  arr == arr.reverse
end

def palindrome_manual?(arr)
  left = 0
  right = arr.length - 1
  
  while left < right
    return false if arr[left] != arr[right]
    left += 1
    right -= 1
  end
  true
end

puts palindrome?([1, 2, 3, 2, 1]) #=> true
puts palindrome?([1, 2, 3, 4, 5]) #=> false

Q27: Find missing number in sequence

Explanation: Given an array containing n-1 numbers from 1 to n, find the missing number.

def find_missing_number(arr)
  n = arr.length + 1
  expected_sum = n * (n + 1) / 2
  actual_sum = arr.sum
  expected_sum - actual_sum
end

def find_missing_number_xor(arr)
  n = arr.length + 1
  xor_all = (1..n).reduce(:^)
  xor_arr = arr.reduce(:^)
  xor_all ^ xor_arr
end

numbers = [1, 2, 4, 5, 6] # missing 3
puts find_missing_number(numbers) #=> 3
puts find_missing_number_xor(numbers) #=> 3

Q28: Find duplicates in array

Explanation: Find elements that appear more than once.

def find_duplicates(arr)
  seen = {}
  duplicates = []
  
  arr.each do |element|
    if seen[element]
      duplicates << element unless duplicates.include?(element)
    else
      seen[element] = true
    end
  end
  
  duplicates
end

def find_all_duplicates(arr)
  arr.group_by { |e| e }
     .select { |_, v| v.size > 1 }
     .keys
end

numbers = [1, 2, 3, 2, 4, 3, 5]
puts find_duplicates(numbers).inspect #=> [2, 3]

Q29: Find the first non-repeating element

Explanation: Find the first element that appears only once.

def first_non_repeating(arr)
  freq = arr.each_with_object(Hash.new(0)) { |e, h| h[e] += 1 }
  arr.find { |element| freq[element] == 1 }
end

numbers = [4, 5, 1, 2, 0, 4, 5, 1]
puts first_non_repeating(numbers) #=> 2

Q30: Find the longest consecutive sequence

Explanation: Find the length of the longest consecutive elements sequence.

def longest_consecutive(nums)
  return 0 if nums.empty?
  
  num_set = nums.to_set
  longest = 0
  
  num_set.each do |num|
    # Only start counting if this is the beginning of a sequence
    if !num_set.include?(num - 1)
      current_num = num
      current_length = 1
      
      while num_set.include?(current_num + 1)
        current_num += 1
        current_length += 1
      end
      
      longest = [longest, current_length].max
    end
  end
  
  longest
end

numbers = [100, 4, 200, 1, 3, 2]
puts longest_consecutive(numbers) #=> 4 (1,2,3,4)

15. String Problems

Q31: Reverse a string without using reverse

Explanation: Multiple approaches to reverse a string manually.

def reverse_string(str)
  reversed = ""
  str.each_char { |c| reversed = c + reversed }
  reversed
end

def reverse_string_indices(str)
  result = ""
  (str.length - 1).downto(0) { |i| result << str[i] }
  result
end

def reverse_string_array(str)
  str.chars.inject([]) { |arr, c| arr.unshift(c) }.join
end

puts reverse_string("hello") #=> "olleh"
puts reverse_string_indices("ruby") #=> "ybur"

Q32: Check if string is palindrome

Explanation: Determine if a string reads the same forwards and backwards.

def palindrome_string?(str)
  str == str.reverse
end

def palindrome_manual?(str)
  left = 0
  right = str.length - 1
  
  while left < right
    return false if str[left] != str[right]
    left += 1
    right -= 1
  end
  true
end

def palindrome_ignore_case?(str)
  cleaned = str.downcase.gsub(/[^a-z0-9]/, '')
  cleaned == cleaned.reverse
end

puts palindrome_string?("racecar") #=> true
puts palindrome_string?("hello") #=> false
puts palindrome_ignore_case?("A man, a plan, a canal: Panama") #=> true

Q33: Count vowels and consonants

Explanation: Count vowels and consonants in a string.

def count_vowels_consonants(str)
  vowels = 'aeiouAEIOU'
  counts = { vowels: 0, consonants: 0 }
  
  str.each_char do |char|
    if char =~ /[a-zA-Z]/
      if vowels.include?(char)
        counts[:vowels] += 1
      else
        counts[:consonants] += 1
      end
    end
  end
  
  counts
end

result = count_vowels_consonants("Hello World")
puts "Vowels: #{result[:vowels]}, Consonants: #{result[:consonants]}"
#=> Vowels: 3, Consonants: 7

16. Hash Problems

Q34: Merge two hashes with conflict resolution

Explanation: Merge hashes with custom handling for duplicate keys.

def merge_hashes(hash1, hash2)
  result = hash1.dup
  hash2.each do |key, value|
    if result.key?(key)
      # Handle conflict - here we choose the larger value
      result[key] = [result[key], value].max
    else
      result[key] = value
    end
  end
  result
end

def merge_with_block(hash1, hash2, &block)
  result = hash1.dup
  hash2.each do |key, value|
    if result.key?(key)
      result[key] = block.call(key, result[key], value)
    else
      result[key] = value
    end
  end
  result
end

h1 = {a: 1, b: 2, c: 3}
h2 = {b: 5, d: 4}

puts merge_hashes(h1, h2).inspect #=> {:a=>1, :b=>5, :c=>3, :d=>4}

# Custom conflict resolution - sum values
merged = merge_with_block(h1, h2) { |key, v1, v2| v1 + v2 }
puts merged.inspect #=> {:a=>1, :b=>7, :c=>3, :d=>4}

Q35: Invert a hash (swap keys and values)

Explanation: Swap keys and values, handling duplicate values.

def invert_hash(hash)
  hash.invert
end

def invert_hash_manual(hash)
  result = {}
  hash.each { |key, value| result[value] = key }
  result
end

def invert_hash_handle_duplicates(hash)
  result = {}
  hash.each do |key, value|
    if result[value]
      result[value] = [result[value]] unless result[value].is_a?(Array)
      result[value] << key
    else
      result[value] = key
    end
  end
  result
end

original = {a: 1, b: 2, c: 2}
puts invert_hash_handle_duplicates(original).inspect 
#=> {1=>:a, 2=>[:b, :c]}

17. Number Problems

Q36: Check if number is prime

Explanation: Determine if a number is prime.

def prime?(n)
  return false if n <= 1
  return true if n <= 3
  return false if n % 2 == 0 || n % 3 == 0
  
  i = 5
  while i * i <= n
    return false if n % i == 0 || n % (i + 2) == 0
    i += 6
  end
  true
end

puts prime?(17) #=> true
puts prime?(15) #=> false

Q37: Find factorial

Explanation: Calculate factorial recursively and iteratively.

def factorial_recursive(n)
  return 1 if n <= 1
  n * factorial_recursive(n - 1)
end

def factorial_iterative(n)
  result = 1
  (2..n).each { |i| result *= i }
  result
end

puts factorial_iterative(5) #=> 120

Q38: Fibonacci sequence

Explanation: Generate Fibonacci sequence.

def fibonacci_recursive(n)
  return n if n <= 1
  fibonacci_recursive(n - 1) + fibonacci_recursive(n - 2)
end

def fibonacci_iterative(n)
  return n if n <= 1
  
  a, b = 0, 1
  (2..n).each do
    a, b = b, a + b
  end
  b
end

def fibonacci_sequence(length)
  return [] if length <= 0
  return [0] if length == 1
  
  result = [0, 1]
  (2...length).each do
    result << result[-1] + result[-2]
  end
  result
end

puts fibonacci_sequence(10).inspect #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

Quick Reference Cheat Sheet

| Problem | Best Approach | Time | Space | |---------|--------------|------|-------| | Flatten array | Recursive | O(n) | O(n) | | Remove nils | select/reject | O(n) | O(n) | | Reverse array | Two pointers | O(n) | O(1) | | Find max | Single pass | O(n) | O(1) | | Second largest | Single pass | O(n) | O(1) | | Remove duplicates | Hash | O(n) | O(n) | | Merge sorted arrays | Two pointers | O(n+m) | O(n+m) | | Rotate array | Slice | O(n) | O(n) | | Find pairs | Hash | O(n) | O(n) | | Move zeroes | Partition | O(n) | O(1) | | Array intersection | Hash set | O(n+m) | O(min(n,m)) | | Frequency count | Hash | O(n) | O(n) |


# Ruby Coding Interview Questions - Simple but Commonly Asked

## Table of Contents
1. [String Basics](#1-string-basics)
2. [Number Operations](#2-number-operations)
3. [Array Basics](#3-array-basics)
4. [Hash Basics](#4-hash-basics)
5. [Conditionals and Loops](#5-conditionals-and-loops)
6. [Simple Algorithms](#6-simple-algorithms)
7. [Common Ruby Methods](#7-common-ruby-methods)
8. [Object Oriented Basics](#8-object-oriented-basics)
9. [Error Handling](#9-error-handling)
10. [File Operations](#10-file-operations)

---

## 1. String Basics

### Q1: Check if a string is empty or nil

**Explanation:**
Very common check in Ruby applications. Need to handle both nil and empty string cases.

```ruby
def blank?(str)
  str.nil? || str.empty?
end

# Ruby's built-in method
def blank_rails?(str)
  str.blank?  # Rails method - true for nil, false, empty
end

# Examples
puts blank?(nil)      #=> true
puts blank?("")       #=> true
puts blank?("hello")  #=> false

Q2: Convert string to integer safely

Explanation: Handle cases where string might not be a valid number.

def safe_to_int(str)
  Integer(str) rescue nil
end

def to_int_with_default(str, default = 0)
  Integer(str) rescue default
end

puts safe_to_int("123")    #=> 123
puts safe_to_int("abc")    #=> nil
puts to_int_with_default("abc", -1)  #=> -1

Q3: Capitalize first letter of each word

Explanation: Title case conversion - common for formatting names, titles.

def title_case(sentence)
  sentence.split.map(&:capitalize).join(' ')
end

def title_case_except_articles(sentence)
  articles = ['a', 'an', 'the', 'and', 'but', 'or', 'for', 'nor', 'on', 'at', 'to', 'by']
  
  sentence.split.each_with_index.map do |word, i|
    if i == 0 || !articles.include?(word.downcase)
      word.capitalize
    else
      word.downcase
    end
  end.join(' ')
end

puts title_case("hello world")           #=> "Hello World"
puts title_case_except_articles("the lord of the rings")  #=> "The Lord of the Rings"

Q4: Remove whitespace from string

Explanation: Different ways to remove whitespace depending on requirements.

# Remove leading/trailing whitespace
def trim(str)
  str.strip
end

# Remove all whitespace
def remove_all_spaces(str)
  str.gsub(/\s+/, '')
end

# Remove extra spaces (single space between words)
def normalize_spaces(str)
  str.gsub(/\s+/, ' ').strip
end

text = "  Hello   World  !  "
puts trim(text)                #=> "Hello   World  !"
puts remove_all_spaces(text)   #=> "HelloWorld!"
puts normalize_spaces(text)    #=> "Hello World !"

Q5: Check if string contains only digits

Explanation: Validate numeric strings for further processing.

def numeric?(str)
  str.match?(/\A\d+\z/)
end

def integer?(str)
  str.match?(/\A-?\d+\z/)
end

def decimal?(str)
  str.match?(/\A-?\d+(\.\d+)?\z/)
end

puts numeric?("123")      #=> true
puts numeric?("12.3")     #=> false
puts integer?("-123")     #=> true
puts decimal?("123.45")   #=> true

Q6: Count occurrences of a character

Explanation: Count how many times a specific character appears.

def count_char(str, char)
  str.count(char)
end

def count_char_manual(str, char)
  str.chars.count { |c| c == char }
end

def count_char_regex(str, char)
  str.scan(/#{char}/).length
end

text = "hello world"
puts count_char(text, 'l')      #=> 3
puts count_char_manual(text, 'o') #=> 2

Q7: Reverse words in a sentence

Explanation: Reverse the order of words, not the characters.

def reverse_words(sentence)
  sentence.split.reverse.join(' ')
end

def reverse_words_manual(sentence)
  words = sentence.split
  reversed = []
  
  (words.length - 1).downto(0) do |i|
    reversed << words[i]
  end
  
  reversed.join(' ')
end

puts reverse_words("Hello World Ruby")  #=> "Ruby World Hello"

Q8: Check if two strings are anagrams

Explanation: Determine if two strings use the same letters.

def anagrams?(str1, str2)
  str1.downcase.chars.sort == str2.downcase.chars.sort
end

def anagrams_manual?(str1, str2)
  return false if str1.length != str2.length
  
  str1.downcase.each_char do |char|
    return false unless str2.downcase.include?(char)
    str2 = str2.sub(char, '')
  end
  true
end

puts anagrams?("listen", "silent")    #=> true
puts anagrams?("hello", "world")      #=> false

2. Number Operations

Q9: Check if number is even or odd

Explanation: Basic parity check - very common in conditional logic.

def even?(num)
  num % 2 == 0
end

def odd?(num)
  num % 2 != 0
end

# Using bitwise AND
def even_bitwise?(num)
  num & 1 == 0
end

puts even?(4)   #=> true
puts odd?(5)    #=> true
puts even?(7)   #=> false

Q10: Find sum of digits in a number

Explanation: Break down number and add individual digits.

def sum_of_digits(num)
  num.to_s.chars.map(&:to_i).sum
end

def sum_of_digits_manual(num)
  sum = 0
  num.abs.to_s.each_char { |digit| sum += digit.to_i }
  sum
end

def sum_of_digits_math(num)
  num = num.abs
  sum = 0
  while num > 0
    sum += num % 10
    num /= 10
  end
  sum
end

puts sum_of_digits(123)    #=> 6 (1+2+3)
puts sum_of_digits_math(456) #=> 15 (4+5+6)

Q11: Generate multiplication table

Explanation: Simple table generation - tests loops and formatting.

def multiplication_table(num, upto = 10)
  (1..upto).map { |i| "#{num} × #{i} = #{num * i}" }
end

def print_table(num, upto = 10)
  multiplication_table(num, upto).each { |line| puts line }
end

puts multiplication_table(5)
#=> ["5 × 1 = 5", "5 × 2 = 10", "5 × 3 = 15", ...]

print_table(7, 5)
# Output:
# 7 × 1 = 7
# 7 × 2 = 14
# 7 × 3 = 21
# 7 × 4 = 28
# 7 × 5 = 35

Q12: Check if a number is positive, negative, or zero

Explanation: Basic number classification.

def number_type(num)
  if num > 0
    "positive"
  elsif num < 0
    "negative"
  else
    "zero"
  end
end

# Using case statement
def number_type_case(num)
  case num <=> 0
  when 1 then "positive"
  when -1 then "negative"
  else "zero"
  end
end

puts number_type(5)    #=> "positive"
puts number_type(-3)   #=> "negative"
puts number_type(0)    #=> "zero"

Q13: Find factorial of a number (simple)

Explanation: Basic factorial calculation - tests recursion or iteration.

def factorial(n)
  return 1 if n <= 1
  n * factorial(n - 1)
end

def factorial_iterative(n)
  result = 1
  (2..n).each { |i| result *= i }
  result
end

puts factorial(5)    #=> 120
puts factorial(0)    #=> 1
puts factorial(1)    #=> 1

Q14: Generate Fibonacci series up to n

Explanation: Classic sequence generation problem.

def fibonacci_upto(n)
  return [] if n <= 0
  return [0] if n == 1
  
  fib = [0, 1]
  while fib[-1] + fib[-2] <= n
    fib << fib[-1] + fib[-2]
  end
  fib
end

def first_n_fibonacci(n)
  return [] if n <= 0
  return [0] if n == 1
  
  fib = [0, 1]
  (2...n).each { fib << fib[-1] + fib[-2] }
  fib
end

puts fibonacci_upto(50).inspect   #=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
puts first_n_fibonacci(7).inspect #=> [0, 1, 1, 2, 3, 5, 8]

Q15: Find greatest common divisor (GCD)

Explanation: Euclidean algorithm for finding GCD.

def gcd(a, b)
  while b != 0
    a, b = b, a % b
  end
  a
end

def gcd_recursive(a, b)
  return a if b == 0
  gcd_recursive(b, a % b)
end

puts gcd(48, 18)    #=> 6
puts gcd(56, 42)    #=> 14
puts gcd(17, 19)    #=> 1

3. Array Basics

Q16: Check if element exists in array

Explanation: Basic array membership test.

def element_exists?(arr, element)
  arr.include?(element)
end

def element_exists_manual?(arr, element)
  arr.each { |e| return true if e == element }
  false
end

numbers = [1, 2, 3, 4, 5]
puts element_exists?(numbers, 3)  #=> true
puts element_exists?(numbers, 6)  #=> false

Q17: Get first and last element of array

Explanation: Accessing array boundaries safely.

def first_element(arr)
  arr.first
end

def last_element(arr)
  arr.last
end

def safe_first(arr, default = nil)
  arr.empty? ? default : arr.first
end

def safe_last(arr, default = nil)
  arr.empty? ? default : arr.last
end

numbers = [1, 2, 3]
puts first_element(numbers)    #=> 1
puts last_element(numbers)     #=> 3
puts safe_first([], "empty")   #=> "empty"

Q18: Add element to beginning and end

Explanation: Different ways to add elements to arrays.

def add_to_beginning(arr, element)
  [element] + arr
end

def add_to_end(arr, element)
  arr + [element]
end

def add_to_beginning!(arr, element)
  arr.unshift(element)
end

def add_to_end!(arr, element)
  arr << element
end

numbers = [2, 3, 4]
puts add_to_beginning(numbers, 1).inspect  #=> [1, 2, 3, 4]
puts add_to_end(numbers, 5).inspect        #=> [2, 3, 4, 5]

Q19: Remove duplicates (simple version)

Explanation: Basic duplicate removal - different from earlier advanced version.

def remove_duplicates(arr)
  arr.uniq
end

def remove_duplicates_manual(arr)
  result = []
  arr.each { |e| result << e unless result.include?(e) }
  result
end

numbers = [1, 2, 2, 3, 3, 3, 4]
puts remove_duplicates(numbers).inspect  #=> [1, 2, 3, 4]

Q20: Check if array is empty

Explanation: Simple but essential check.

def array_empty?(arr)
  arr.empty?
end

def array_empty_manual?(arr)
  arr.length == 0
end

puts array_empty?([])      #=> true
puts array_empty?([1, 2])  #=> false

Q21: Combine two arrays

Explanation: Different ways to combine arrays.

def combine_arrays(arr1, arr2)
  arr1 + arr2
end

def combine_without_duplicates(arr1, arr2)
  (arr1 + arr2).uniq
end

def combine_alternating(arr1, arr2)
  result = []
  max_length = [arr1.length, arr2.length].max
  
  (0...max_length).each do |i|
    result << arr1[i] if i < arr1.length
    result << arr2[i] if i < arr2.length
  end
  result
end

a1 = [1, 2, 3]
a2 = [4, 5, 6]
puts combine_arrays(a1, a2).inspect           #=> [1, 2, 3, 4, 5, 6]
puts combine_alternating([1, 3], [2, 4]).inspect #=> [1, 2, 3, 4]

4. Hash Basics

Q22: Check if key exists in hash

Explanation: Basic hash key lookup.

def key_exists?(hash, key)
  hash.key?(key)
end

def key_exists_manual?(hash, key)
  hash.each_key { |k| return true if k == key }
  false
end

person = { name: "John", age: 30 }
puts key_exists?(person, :name)   #=> true
puts key_exists?(person, :city)   #=> false

Q23: Get all keys or values from hash

Explanation: Extracting hash components.

def get_keys(hash)
  hash.keys
end

def get_values(hash)
  hash.values
end

def get_keys_manual(hash)
  keys = []
  hash.each { |k, _| keys << k }
  keys
end

person = { name: "John", age: 30, city: "NYC" }
puts get_keys(person).inspect   #=> [:name, :age, :city]
puts get_values(person).inspect #=> ["John", 30, "NYC"]

Q24: Create hash from two arrays

Explanation: Zip arrays into key-value pairs.

def arrays_to_hash(keys, values)
  Hash[keys.zip(values)]
end

def arrays_to_hash_manual(keys, values)
  hash = {}
  keys.each_with_index do |key, i|
    hash[key] = values[i] if i < values.length
  end
  hash
end

keys = [:name, :age, :city]
values = ["John", 30, "NYC"]
puts arrays_to_hash(keys, values).inspect 
#=> {:name=>"John", :age=>30, :city=>"NYC"}

Q25: Merge two hashes (simple)

Explanation: Combining hashes with basic conflict handling.

def merge_hashes(h1, h2)
  h1.merge(h2)
end

def merge_hashes_manual(h1, h2)
  result = h1.dup
  h2.each { |k, v| result[k] = v }
  result
end

h1 = {a: 1, b: 2}
h2 = {b: 3, c: 4}
puts merge_hashes(h1, h2).inspect  #=> {:a=>1, :b=>3, :c=>4}

Q26: Count frequency of words (simple)

Explanation: Basic word counting in a sentence.

def word_frequency(sentence)
  words = sentence.downcase.split
  frequency = {}
  
  words.each do |word|
    if frequency[word]
      frequency[word] += 1
    else
      frequency[word] = 1
    end
  end
  
  frequency
end

sentence = "the cat and the dog"
puts word_frequency(sentence).inspect 
#=> {"the"=>2, "cat"=>1, "and"=>1, "dog"=>1}

5. Conditionals and Loops

Q27: FizzBuzz (classic)

Explanation: The classic interview problem - tests basic logic flow.

def fizzbuzz(n)
  (1..n).map do |i|
    if i % 15 == 0
      "FizzBuzz"
    elsif i % 3 == 0
      "Fizz"
    elsif i % 5 == 0
      "Buzz"
    else
      i
    end
  end
end

puts fizzbuzz(15).inspect
#=> [1, 2, "Fizz", 4, "Buzz", "Fizz", 7, 8, "Fizz", "Buzz", 11, "Fizz", 13, 14, "FizzBuzz"]

Q28: Print numbers 1 to 10 with different loops

Explanation: Demonstrates various looping constructs.

# Using each
def print_with_each
  (1..10).each { |n| puts n }
end

# Using for loop
def print_with_for
  for i in 1..10
    puts i
  end
end

# Using while
def print_with_while
  i = 1
  while i <= 10
    puts i
    i += 1
  end
end

# Using until
def print_with_until
  i = 1
  until i > 10
    puts i
    i += 1
  end
end

# Using times
def print_with_times
  10.times { |i| puts i + 1 }
end

Q29: Sum of numbers in range

Explanation: Calculate sum using different approaches.

def sum_range(start, finish)
  (start..finish).sum
end

def sum_range_manual(start, finish)
  sum = 0
  (start..finish).each { |n| sum += n }
  sum
end

def sum_range_formula(start, finish)
  n = finish - start + 1
  n * (start + finish) / 2
end

puts sum_range(1, 10)        #=> 55
puts sum_range_formula(1, 10) #=> 55

Q30: Check if number is in range

Explanation: Range inclusion test.

def in_range?(num, min, max)
  num.between?(min, max)
end

def in_range_manual?(num, min, max)
  num >= min && num <= max
end

puts in_range?(5, 1, 10)   #=> true
puts in_range?(15, 1, 10)  #=> false

6. Simple Algorithms

Q31: Find minimum and maximum in array (simple)

Explanation: Basic min/max without using built-in methods.

def find_min(arr)
  return nil if arr.empty?
  min = arr.first
  arr.each { |n| min = n if n < min }
  min
end

def find_max(arr)
  return nil if arr.empty?
  max = arr.first
  arr.each { |n| max = n if n > max }
  max
end

numbers = [3, 7, 2, 9, 1, 5]
puts find_min(numbers)  #=> 1
puts find_max(numbers)  #=> 9

Q32: Count even and odd numbers

Explanation: Simple classification and counting.

def count_even_odd(arr)
  even_count = 0
  odd_count = 0
  
  arr.each do |n|
    if n.even?
      even_count += 1
    else
      odd_count += 1
    end
  end
  
  { even: even_count, odd: odd_count }
end

numbers = [1, 2, 3, 4, 5, 6]
puts count_even_odd(numbers).inspect  #=> {:even=>3, :odd=>3}

Q33: Reverse a number

Explanation: Reverse digits of a number.

def reverse_number(num)
  num.to_s.reverse.to_i
end

def reverse_number_math(num)
  reversed = 0
  while num > 0
    reversed = reversed * 10 + num % 10
    num /= 10
  end
  reversed
end

puts reverse_number(12345)   #=> 54321
puts reverse_number_math(123) #=> 321

Q34: Check if year is leap year

Explanation: Leap year calculation - tests nested conditions.

def leap_year?(year)
  if year % 400 == 0
    true
  elsif year % 100 == 0
    false
  elsif year % 4 == 0
    true
  else
    false
  end
end

# More concise
def leap_year_compact?(year)
  (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)
end

puts leap_year?(2000)  #=> true
puts leap_year?(1900)  #=> false
puts leap_year?(2024)  #=> true

Q35: Generate random password

Explanation: Simple string generation with character sets.

def random_password(length = 8)
  chars = [('a'..'z'), ('A'..'Z'), (0..9)].map(&:to_a).flatten
  (0...length).map { chars[rand(chars.length)] }.join
end

def simple_password(length = 8)
  ('a'..'z').to_a.sample(length).join
end

puts random_password     #=> "kL9mN2pQ"
puts simple_password     #=> "fgkmpqrs"

7. Common Ruby Methods

Q36: Implement times method

Explanation: Recreate Ruby's built-in times method.

def my_times(n, &block)
  i = 0
  while i < n
    block.call(i)
    i += 1
  end
  n
end

# Usage
my_times(3) { |i| puts "Hello #{i}" }
# Output:
# Hello 0
# Hello 1
# Hello 2

Q37: Implement each method for array

Explanation: Basic iterator implementation.

def my_each(array, &block)
  i = 0
  while i < array.length
    block.call(array[i])
    i += 1
  end
  array
end

my_each([1, 2, 3]) { |n| puts n * 2 }
# Output: 2, 4, 6

Q38: Implement select method

Explanation: Filtering array based on condition.

def my_select(array, &block)
  result = []
  array.each { |element| result << element if block.call(element) }
  result
end

numbers = [1, 2, 3, 4, 5]
evens = my_select(numbers) { |n| n.even? }
puts evens.inspect  #=> [2, 4]

Q39: Implement map method

Explanation: Transforming array elements.

def my_map(array, &block)
  result = []
  array.each { |element| result << block.call(element) }
  result
end

numbers = [1, 2, 3]
doubled = my_map(numbers) { |n| n * 2 }
puts doubled.inspect  #=> [2, 4, 6]

8. Object Oriented Basics

Q40: Create a simple Person class

Explanation: Basic class with attributes and methods.

class Person
  attr_accessor :name, :age
  
  def initialize(name, age)
    @name = name
    @age = age
  end
  
  def introduce
    "Hi, I'm #{@name} and I'm #{@age} years old"
  end
  
  def birthday!
    @age += 1
  end
  
  def older_than?(other)
    @age > other.age
  end
end

person1 = Person.new("Alice", 25)
person2 = Person.new("Bob", 30)

puts person1.introduce        #=> "Hi, I'm Alice and I'm 25 years old"
person1.birthday!
puts person1.age              #=> 26
puts person1.older_than?(person2)  #=> false

Q41: Create a simple Bank Account class

Explanation: Class with state and validation.

class BankAccount
  attr_reader :balance, :account_number
  
  def initialize(initial_balance = 0)
    @balance = initial_balance
    @account_number = rand(10000..99999)
  end
  
  def deposit(amount)
    if amount > 0
      @balance += amount
      true
    else
      false
    end
  end
  
  def withdraw(amount)
    if amount > 0 && amount <= @balance
      @balance -= amount
      true
    else
      false
    end
  end
  
  def display
    "Account ##{@account_number}: $#{@balance}"
  end
end

account = BankAccount.new(100)
account.deposit(50)
account.withdraw(30)
puts account.display  #=> "Account #12345: $120"

Q42: Create a simple Counter class

Explanation: Class with class and instance variables.

class Counter
  @@total_count = 0
  
  def initialize
    @count = 0
    @@total_count += 1
  end
  
  def increment
    @count += 1
  end
  
  def decrement
    @count -= 1 if @count > 0
  end
  
  def value
    @count
  end
  
  def self.total_instances
    @@total_count
  end
end

c1 = Counter.new
c2 = Counter.new
c1.increment
c1.increment
c2.increment

puts c1.value              #=> 2
puts c2.value              #=> 1
puts Counter.total_instances  #=> 2

9. Error Handling

Q43: Safe division with error handling

Explanation: Handle division by zero gracefully.

def safe_divide(a, b)
  begin
    a / b
  rescue ZeroDivisionError
    "Cannot divide by zero"
  end
end

def safe_divide_with_default(a, b, default = nil)
  a / b
rescue ZeroDivisionError
  default
end

puts safe_divide(10, 2)      #=> 5
puts safe_divide(10, 0)      #=> "Cannot divide by zero"
puts safe_divide_with_default(10, 0, 0)  #=> 0

Q44: Convert to integer with error handling

Explanation: Handle conversion errors.

def to_integer(value)
  Integer(value)
rescue ArgumentError, TypeError
  nil
end

def to_integer_with_default(value, default = 0)
  Integer(value)
rescue ArgumentError, TypeError
  default
end

puts to_integer("123")      #=> 123
puts to_integer("abc")      #=> nil
puts to_integer_with_default("abc", -1)  #=> -1

10. File Operations

Q45: Read a file line by line

Explanation: Basic file reading operations.

def read_file(filename)
  File.readlines(filename) if File.exist?(filename)
rescue Errno::ENOENT
  "File not found"
end

def print_file(filename)
  File.foreach(filename) { |line| puts line } if File.exist?(filename)
end

# Count lines in file
def count_lines(filename)
  return 0 unless File.exist?(filename)
  File.foreach(filename).count
end

Q46: Write to a file

Explanation: Basic file writing operations.

def write_to_file(filename, content)
  File.write(filename, content)
  true
rescue IOError => e
  puts "Error writing file: #{e.message}"
  false
end

def append_to_file(filename, content)
  File.open(filename, 'a') { |f| f.puts(content) }
  true
rescue IOError => e
  puts "Error appending to file: #{e.message}"
  false
end

Q47: Check if file exists

Explanation: File existence check.

def file_exists?(filename)
  File.exist?(filename)
end

def file_readable?(filename)
  File.readable?(filename)
end

def file_writable?(filename)
  File.writable?(filename)
end

puts file_exists?("test.txt")    #=> true/false

Quick Reference - Common Patterns

| Pattern | Code | |---------|------| | Safe nil check | value.nil? ? default : value | | Safe method call | object&.method | | Default value | variable ||= default | | Ternary operator | condition ? true_value : false_value | | Loop with index | array.each_with_index { \|item, i\| ... } | | Transform array | array.map { \|x\| x * 2 } | | Filter array | array.select { \|x\| x > 5 } | | Find element | array.find { \|x\| x == target } | | Group by | array.group_by { \|x\| x % 2 } | | Sum array | array.sum or array.reduce(:+) |


Most Commonly Asked One-Liners

# Check if string is numeric
string.match?(/\A\d+\z/)

# Convert string to array of characters
string.chars

# Convert array to string with delimiter
array.join(', ')

# Get unique elements
array.uniq

# Sort array
array.sort

# Reverse string/array
string.reverse / array.reverse

# Check if all elements meet condition
array.all? { |x| x > 0 }

# Check if any element meets condition
array.any? { |x| x.even? }

# Get first n elements
array.first(n)

# Get last n elements
array.last(n)

# Remove nil values
array.compact

# Flatten one level
array.flatten(1)