Natalia Natalia - 4 months ago 11
Ruby Question

In Ruby, how can I merge consecutive tokens in an array subject to a condition?

This is with Ruby 2.4. I have an array of strings and I want to fuse consecutive elements if they begin or end with a special character. So, for instance, if my special character is "&" and I have an array

["a &", "b", "c", "d"]


I want the result to be

["a & b", "c", "d"]


Similarly, if the array is

["a", "&", "b", "c"]


I want the result to be

["a & b", "c"]


And if the array is

["a", "& b", "c"]


I want the result to be

["a & b", "c"]


However, if the array is

["&", "b", "c"]


The result should be

["&", "b", "c"]


because there is no non-special element preceding my special character. So I have tried this as a solution

2.4.0 :012 > words = ["a", "&", "b", "d"]
=> ["a", "&", "b", "d"]
2.4.0 :013 > SPECIAL_TOKENS = %w(&).freeze
=> ["&"]
2.4.0 :014 > words = words.chunk_while { |i, _| i.end_with?(*SPECIAL_TOKENS) }.map(&:join)
=> ["a", "&b", "d"]


but as you can see, it is not fusing the first element in with the rest (also I'm losing a space between the "&" and the "b"). Are there any adjustments I can make to the above to make it work as I expect?

Answer Source
def join_some(arr, join_ch='&')
  arr.drop(1).each_with_object([arr.first]).with_index(1) do |(s,a),i|
    if (s[0] == join_ch && (i < arr.size - 1 || s.size > 1)) || 
       (a.last[-1] == join_ch && (i > 1 || a.last.size > 1)) 
      a.last << " #{s}"
    else
      a << s
    end
  end
end

join_some ["a", "&", "b", "d"]      #=> ["a & b", "d"]
join_some ["a", "& b", "c"]         #=> ["a & b", "c"]
join_some ["&", "b", "c"]           #=> ["&", "b", "c"]
join_some ["a", "b", "&"]           #=> ["a", "b", "&"]
join_some ["a", "&", "b", "&", "c"] #=> ["a & b & c"]
join_some ["a", "& b &", "c"]       #=> ["a & b & c"]
join_some ["&", "& b", "c"]         #=> ["& & b", "c"]
join_some [" &", "b", "c"]          #=> [" & b", "c"]
join_some ["&", "&", "&"]           #=> ["& & &"]
join_some ["a", "+ b", "+ c"], "+"  #=> ["a + b + c"]

I am not certain if the return values from the last several examples are what is wanted.