Splitting an array using each_with_slice but keeping specific value(s) in resultant arrays (one liner)

Viewed 72

I'm hoping to get an array into a specific format and do so in a one-liner if possible. Using each_slice(2).to_a is helpful in splitting the array, however I would like to keep second coordinate of each array as the first coordinate of the next array.

Initial Array

# format is [x1, y1, x2, y2, x3, y3, x4, y4]...

full_line_coords = [[1, 1, 2, 2, 3, 3, 4, 4], [5, 5, 6, 6, 7, 7, 8, 8]]

Desired Output

# desired format is [[[x1, y1], [x2, y2]], [[x2, y2], [x3, y3]], [[x3, y3], [x4, y4]]]...
desired = [[[1, 1], [2, 2]], [[2, 2], [3, 3]], [[3, 3], [4, 4]]]

Success without one-liner

# without one line:

desired = []
temp_array = []

full_line_coords.each do |x|
  temp_array << x.each_slice(2).to_a
end

temp_array.each do |x|
  i = 0
  until i == x.length - 1
    desired << [x[i], x[i+1]]
    i += 1
  end
end

p desired

# => [[[1, 1], [2, 2]], [[2, 2], [3, 3]], [[3, 3], [4, 4]], [[5, 5], [6, 6]], [[6, 6], [7, 7]], [[7, 7], [8, 8]]]

Unsure how to make this as one-line, found it simple enough to do the split, but not keeping the end/start coordinates in each array (as below).

One-liner attempt

attempt = full_line_coords.each { |x| p x.each_slice(2).to_a.each_slice(2).to_a } # p to show this is where i'd like the array to be in 'desired' format if possible.

# => [[[1, 1], [2, 2]], [[3, 3], [4, 4]]]
#    [[[5, 5], [6, 6]], [[7, 7], [8, 8]]]

Background/Reasoning

The only reason I wish to keep it is a one-liner is because I want to return the "parent" object itself, not just the resulting attributes.

"Parent" objects being a list of links: #<Link:0x803a2e8>, with many attributes, including "segments".

links.each do |l|
  puts l.segments
end

# Gives an array of XY coordinates, including all vertices. e.g. [1, 1, 2, 2, 3, 3, 4, 4]

I am then looking to use the "desired" output in some other defined methods but return the link object itself at the end #<Link:0x803a2e8>, not just products from the link's attributes.

Many thanks.

2 Answers

This is the first option I found:

full_line_coords.flat_map { |e| e.each_slice(2).each_cons(2).to_a } 

Find the methods in Enumerable class and Array class

Input

full_line_coords = [[1, 1, 2, 2, 3, 3, 4, 4], [5, 5, 6, 6, 7, 7, 8, 8]]

Code

p full_line_coords.map { |var| var.slice_when { |x, y| x != y }
                                   .each_cons(2)
                                   .to_enum
                                   .map(&:itself) }

Output

[[[[1, 1], [2, 2]], [[2, 2], [3, 3]], [[3, 3], [4, 4]]], [[[5, 5], [6, 6]], [[6, 6], [7, 7]], [[7, 7], [8, 8]]]]
Related