Alex Riabukha
Alex Riabukha

Reputation: 307

Insert data from hash into an array at set index in Ruby

I’m doing Ruby task, which is “You have an array of numbers. Your task is to sort ascending odd numbers but even numbers must be on their places. Zero isn't an odd number and you don't need to move it. If you have an empty array, you need to return it”.

Decided to split initial array: odd nums pushed into another array, sorted it and even numbers added to hash, where the key is num and its initial index is value. After that, trying to insert the even nums from hash to odd array, at initial indexes of even nums. So, the code looks like this:

def sort_array(source_array)
  even_nums = Hash.new
  odd_nums = []
  return source_array if source_array.length == 0 
  source_array.each_with_index {|n, ind| even_nums[n] = ind if n.even?}
  source_array.select{|n| odd_nums.push(n) if n.odd?}

  odd_nums.sort!

  even_nums.each do |k, v|
     odd_nums.insert(v, k)
  end
  odd_nums
end

With small array like [5, 3, 2, 8, 1, 4, 11] it works as expected but if I pass something bigger like [84, -64, 40, 53, 5, 88, 2, 14, 29, -79, -44, -23, 20, -67, -12, 28, -28, -37, -27, -62, -54, 93, -61, 50, 65, -63, -62, 77, -16, 49, -43, 26, -73, -27, 88, -88, -62, 84, 54, 25, 25, -2, -99, 69, -23, 47, -92, 7, -62, -62, -58, -30, -75, -31, 65, -63, 16, 64, -7, -22, -6, -82]

I’m getting nils at the end of the sorted array. Like this:

[-99, -64, 40, -79, -75, -73, 2, 14, -67, -63, -44, -63, 20, -61, -12, 28, -28, -43, -37, -31, -54, -27, -27, 50, -23, -23, -7, 5, -16, 7, 25, 26, 25, 29, 47, -88, 49, 53, 54, 65, 65, -2, 69, 77, 93, nil, -92, nil, nil, 88, -58, -30, nil, nil, nil, nil, 16, 64, nil, -22, -6, -82, 84, nil, -62]

Struggling to understand, why it isn’t working with bigger arrays?

Upvotes: 1

Views: 277

Answers (3)

Todd A. Jacobs
Todd A. Jacobs

Reputation: 84343

I think this other answer is simpler and more elegant than mine, but this works too. Notably, this solution would allow you to validate the position of your even numbers (for example, in a spec) by looking up the indexes and values in evens. Unless you already know what the output array should look like, this may matter when it comes time to debug the interim results.

def odd_sorted array
  odds  = array.select { |e| e.odd? }.sort
  evens = array.each_with_index.select { |e| e.first.even? }

  arr2 = Array.new(array.size)

  # put even numbers in at their previous index
  evens.each do |e|
    arr2.each_index { |i| arr2[i] = e[0] if e[1] == i }
  end

  arr2.each_with_index { |e, i| arr2[i] = odds.shift if e.nil? }
end

odd_sorted [5, 3, 2, 8, 1, 4, 11]
#=> [1, 3, 2, 8, 5, 4, 11]

odd_sorted [84, -64, 40, 53, 5, 88, 2, 14, 29, -79, -44, -23, 20]
#=> [84, -64, 40, -79, -23, 88, 2, 14, 5, 29, -44, 53, 20]

The Array#map solution is definitely more elegant, but this is (in my personal opinion) more debuggable. Your mileage in that regard will certainly vary.

Upvotes: 2

Cary Swoveland
Cary Swoveland

Reputation: 110675

That can be done as follows.

def sort_odds(arr)
  odd_pos = arr.each_index.select { |i| arr[i].odd? }
  odd_pos.zip(odd_pos.sort_by { |i| arr[i] }).
          each_with_object(arr.dup) do |(old_pos,new_pos),a|
            a[new_pos] = arr[old_pos]
          end
end
sort_odds [5, 3, 2, 8, 1, 4, 11]
  #        o  o  e  e  o  e   o
  #=>     [1, 3, 2, 8, 5, 4, 11]

The steps are as follows.

arr = [5, 3, 2, 8, 1, 4, 11]
  #    o  o  e  e  o  e   o
odd_pos = arr.each_index.select { |i| arr[i].odd? }
  #=> [0, 1,       4,     6]
new_pos = odd_pos.zip(odd_pos.sort_by { |i| arr[i] })
  #=> [[0, 4], [1, 1], [4, 0], [6, 6]]
new_pos.each_with_object(arr.dup) do|(old_pos,new_pos),a|
  a[new_pos] = arr[old_pos]
end
  #=> [1, 3, 2, 8, 5, 4, 11]

Upvotes: 2

tadman
tadman

Reputation: 211560

There's a fairly easy way to do this if you think about it as two operations:

def sort_array(arr)
  # Extract and sort the odd values
  odd = arr.select(&:odd?).sort

  # Merge the sorted odd values back in
  arr.map do |v|
    v.odd? ? odd.shift : v
  end
end

Not much to it.

You had some of the right pieces, but I think you got stuck in the weeds when it started to get overly complicated.

Upvotes: 5

Related Questions