Split array by consecutive sequences [closed] - ruby

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 9 years ago.
Given an array of integer such as:
array = [1,2,3,5,6,7,20,21,33,87,88,89,101]
This array contains k consecutive subsequences (in this case k = 6), such as "1,2,3" and "87,88,89". How do I get an array containing arrays of these k subsequences?
For the above example these would be:
[[1,2,3], [5,6,7], [20,21], [33], [87,88,89], [101]]

The unshifted 0 is a dummy. It could be any number.
[1, 2, 3, 5, 6, 7]
.unshift(0)
.each_cons(2).slice_before{|m, n| m + 1 < n}.map{|a| a.map(&:last)}
# => [[1, 2, 3], [5, 6, 7]]

EDIT:
If you are using rails, there is an easy way, using the active support split method
def sort_it(arr)
(arr.first..arr.last).to_a.split {|i| !arr.include?(i) }.select &:present?
end
or in Ruby
def sort_it(arr)
tmp, main = [], []
arr.each_with_index do |x, i|
if arr[i-1]
if arr[i-1] + 1 == x
tmp << x
else
main << tmp unless tmp.empty?
tmp = [x]
end
else
tmp << x
end
end
main << tmp
main
end
> a = [1, 3, 5, 6, 8, 9, 11, 12, 13, 14, 16, 27]
> sort_it(a)
=> [[1], [3], [5, 6], [8, 9], [11, 12, 13, 14], [16], [27]]

def seq_sort(array)
finished = [array.first], i = 0
array.each_cons(2) do |(a,b)|
if b - a == 1
finished[i] << b
else
finished[i += 1] = [b]
end
end
finished
end
> seq_sort([1,2,38,39,92,94,103,104,105,1002])
=> [[1,2], [38, 39], [92], [94], [103, 104, 105], [1002]]

Related

Issue returning proper value

def sum_two(arry, sum)
p check_sums(sum, arry[0], arry[1..arry.length - 1])
end
def check_sums(target, first_num, remaining_nums)
result = []
return result if remaining_nums == []
remaining_nums.each do |n|
if first_num + n == target
result << [first_num, n]
end
end
check_sums(target, remaining_nums[0], remaining_nums[1..remaining_nums.length - 1])
end
my_arry = [2,4,6,1,3,5,7]
my_sum = 6
sum_two(my_arry, my_sum)
Above is my solution to a practice interview question. However, the output is always an empty array ([]). My question is seemingly rudimentary as I just need to return the final result array so I must be missing something obvious. Basically, I can't figure out why its printing an empty array because I feel quite confident the logic is sound.
UPDATE:
Below is an updated version of my solution in which I wrap the methods in a class and make result an instance variable so that I can maintain its state throughout the recursive call. Thanks to #BenE for mentioning that I was resetting the value every time the recursive call went through. That really cleared it up for me! Here's my new solution:
class SumTwo
#result = []
def self.sum_two(arry, sum)
p SumTwo.check_sums(sum, arry[0], arry[1..arry.length - 1])
end
def self.check_sums(target, first_num, remaining_nums)
return #result if remaining_nums == []
remaining_nums.each do |n|
if first_num + n == target
#result << [first_num, n]
end
end
check_sums(target, remaining_nums[0], remaining_nums[1..remaining_nums.length - 1])
#result
end
end
my_arry = [2,4,6,1,3,5,7]
my_sum = 6
SumTwo.sum_two(my_arry, my_sum)
The problem is that you don't return the result array that you loop on, you only return it when remaning_nums is empty, here is a working solution to you code:
def sum_two(arry, sum)
p check_sums(sum, arry[0], arry[1..arry.length - 1],[])
end
def check_sums(target, first_num, remaining_nums,result)
return result if remaining_nums == []
remaining_nums.each do |n|
if first_num + n == target
result << [first_num, n]
end
end
check_sums(target, remaining_nums[0], remaining_nums[1..remaining_nums.length - 1],result)
result
end
my_arry = [2,4,6,1,3,5,7]
my_sum = 6
sum_two(my_arry, my_sum)
If you want to return all pairs of numbers in an array whose sum is a given value, I think it's easiest to use the method Array#combination:
def sum_two(arry, sum)
arry.combination(2).select { |i,j| i+j == sum }
end
sum_two [2,4,6,1,3,5,7], 6
#=> [[2, 4], [1, 5]]
sum_two [*(1..24)], 12
#=> [[1, 11], [2, 10], [3, 9], [4, 8], [5, 7]]
sum_two [1,3, 6, 8, 2, 9, 3, 5, 7, 8, 16], 17
#=> [[1, 16], [8, 9], [9, 8]]
If you want to eliminate [8, 9] or [9, 8] in the last example, you could do this:
def sum_two(arry, sum)
arry.uniq.combination(2).select { |i,j| i+j == sum }
end
sum_two [1,3, 6, 8, 2, 9, 3, 5, 7, 8, 16], 17
#=> [[1, 16], [8, 9]]

Array into ranges of consecutive numbers [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I am trying to make a Ruby function that converts an array of unique numbers into ranges of consecutive numbers.
[1, 2, 3, 5, 6, 8, 9] => [(1..3), (5..6), (8..9)]
It doesn't seem too hard, but I want to know if there's better way.
How is this using Enumerable#slice_before?
ar = [1, 2, 3, 5, 6, 8, 9]
prev = ar[0]
p ar.slice_before { |e|
prev, prev2 = e, prev
prev2 + 1 != e
}.map{|a| a[0]..a[-1]}
# >> [1..3, 5..6, 8..9]
ar = [1, 2, 3, 5, 6,7, 8, 9,11]
prev = ar[0]
p ar.slice_before { |e|
prev, prev2 = e, prev
prev2 + 1 != e
}.map{|a| a[0]..a[-1]}
# >> [1..3, 5..9, 11..11]
This is something I wrote a while back when dealing with IP address ranges:
class Array
# [1,2,4,5,6,7,9,13].to_ranges # => [1..2, 4..7, 9..9, 13..13]
# [1,2,4,5,6,7,9,13].to_ranges(true) # => [1..2, 4..7, 9, 13]
def to_ranges(non_ranges_ok=false)
self.sort.each_with_index.chunk { |x, i| x - i }.map { |diff, pairs|
if (non_ranges_ok)
pairs.first[0] == pairs.last[0] ? pairs.first[0] : pairs.first[0] .. pairs.last[0]
else
pairs.first[0] .. pairs.last[0]
end
}
end
end
if ($0 == __FILE__)
require 'awesome_print'
ary = [1, 2, 4, 5, 6, 7, 9, 13, 12]
puts ary.join(', ')
ap ary.to_ranges
ary = [1, 2, 4, 8, 5, 6, 7, 3, 9, 11, 12, 10]
puts ary.join(', ')
ap ary.to_ranges
end
Pass true to to_ranges and it will not convert individual elements into one-element ranges.
Here's a solution to the same question you're asking. The linked code does a bit more work than you require (the numbers don't need to be sorted, or consecutive), but it'll do the trick. Or, you could use this code, suggested by #NewAlexandria :
class Array
def to_ranges
compact.sort.uniq.inject([]) do |r,x|
r.empty? || r.last.last.succ != x ? r << (x..x) : r[0..-2] << (r.last.first..x)
end
end
end

Given integers how do I find asc and desc sequences of three?

I have integers i.e. 9, 5, 4, 3, 1, 6, 7, 8. I want to return the index where a sequence of three descending or ascending integers exists. In the example above I would get indices 1 and 5. What is the ruby code for this?
def seq
array = [9,5,4,3,1,6,7,8]
array.each_with_index |val, index|
if (val < (array[index + 1]).val < (array[index + 1]).val)
puts "#{index}"
# Skip two indexes
end
end
I think the logic behind your solution is almost correct, but your syntax is pretty far off from valid Ruby.
Here are a pair of pretty verbose solutions that will (hopefully) be fairly obvious:
numbers = [9, 6, 5, 4, 3, 1, 6, 7, 8]
# Find non-overlapping sets
i = 0
until i > numbers.length - 2
a, b, c = numbers[i..i + 2]
if (a - b == b - c) && (a - b).abs == 1
puts "#{i} (#{a},#{b},#{c})"
# Skip next two indexes
i += 3
else
i += 1
end
end
# Find overlapping sets (same solution, but don't skip indexes)
(0...numbers.length - 2).each do |i|
a, b, c = numbers[i..i + 2]
if (a - b == b - c) && (a - b).abs == 1
puts "#{i} (#{a},#{b},#{c})"
end
end
Since the question is not clear enough. I will assume the question is about finding 3 ascending or descending continuous numbers. If the length of the satisfied sequence it longer than 3, e.g [2, 3, 4, 5], it returns 0 and 1.
Here is the algorithm, do list[index] - list[index - 1] for all elements, and repeat it for another time, the answer will be the index of 0 elements after the calculation.
Intuitively,
original 9, 5, 4, 3, 1, 6, 7, 8
first pass -4, -1, -1, -2, 5, 1, 1
2nd pass 3, 0, -1, 7, 4, 0 -> the answer will be the indexes of 0's, which is 1, 5
Algorithm:
lst = [9, 5, 4, 3, 1, 6, 7, 8]
lst1 = lst.each_cons(2).map{ |a, b| b-a }
lst2 = lst1.each_cons(2).map{ |a, b| b-a }
result = lst2.each_index.select{|i| lst2[i] == 0}
result = [1, 5]
Here’s a solution using each_cons(3).with_index:
[9,5,4,3,1,6,7,8].each_cons(3).with_index.select { |s, i| s[0] < s[1] && s[1] < s[2] }.map(&:last)

Why am I having trouble creating a custom array method that returns a hash with indices of matching array values? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
I'm trying to monkey patch the Array class with a method that, when passed something like [1, 3, 4, 3, 0, 3, 1], will return a hash with { 1 => [0, 6], 3 => [1, 3, 5] }, where the key is the number we are matching to, and the value is an array with the indices of all matches.
Here's the code I have so far. I can't tell why it's returning something like {1=>[0, 2, 3, 1, 2, 0], 3=>[0, 2, 3, 1, 2, 0], 0=>[0, 2, 3, 1, 2, 0]}:
class Array
def dups
matches = {}
matches_index = []
self.each do |i|
self[(i_index + 1)..-1].each_with_index do |j, j_index|
matches_index << j_index if self[i] == self[j]
end
matches[i] = matches_index
end
matches.keep_if { |key, value| value.length > 1 }
end
end
This is an even shorter version:
class Array
def dups
each_index.group_by{|i| self[i]}.select{|k,v| v.size > 1}
end
end
Your code looks very complicated (and very imperative). A functional approach is way easier:
class Array
def dups
grouped_indexed = each_with_index.group_by(&:first)
Hash[grouped_indexed.map { |x, gs| [x, gs.map(&:last)] }]
end
end
Is this still too complicated? well, yes, but that's because the core is missing some basic abstractions like map_by:
require 'facets'
xs = [1, 3, 4, 3, 0, 3, 1]
xs.each_with_index.to_a.map_by { |x, i| [x, i] }
#= {1=>[0, 6], 3=>[1, 3, 5], 4=>[2], 0=>[4]}
To be honest, even that one-liners is too verbose. With the right abstractions we should be able to write something like this:
require 'awesome_functional_extensions'
xs = [1, 3, 4, 3, 0, 3, 1]
xs.with_index.group_by_pair
#= {1=>[0, 6], 3=>[1, 3, 5], 4=>[2], 0=>[4]}
To improve on Stas S already excellent solution:
class Array
def dups
(self.each_index.group_by {|i| self[i]}).keep_if{|k, v| v.size > 1}
end
end
Which results in an array of only duplicates.
class Array
def dups
matches = {}
self.each_with_index do |v, i|
matches.has_key?(v) ? matches[v] << i : matches[v] = [i]
end
matches
end
end
x = [1, 3, 4, 3, 0, 3, 1]
puts x.dups
Yet another version:
class Array
def dups
to_enum
.with_index
.group_by(&:first)
.select{|_, a| a.length > 1}
.each_value{|a| a.map!(&:last)}
end
end

combining three arrays [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
I have three arrays of arrays. I essentially need them laid on top of each other.
first = [[111, 1], [222, 2], [333, 3]]
second = [[111, 4], [222, 5], [333, 6]]
third = [[111,7], [222, 8], [333, 9]]
Ideally it would be great if the final array looked like this:
final = [[111, 1, 4, 7], [222, 2, 5, 8], [333, 3, 6, 9]]
I looked at the product method in hopes that that could help but no go. I have also tried to loop over all three but I guess I'm not that smart.
Combine them, then group them, then map them to fit your specs:
(first + second + third).group_by(&:first).map { |k, v| [k, *v.map(&:last)] }
Not sure if this is working but I done it at the phone so... And for sure there are better ways to achieve it
first = [[111, 1], [222, 2], [333, 3]]
second = [[111, 4], [222, 5], [333, 6]]
third = [[111,7], [222, 8], [333, 9]]
all = [first, second, third]
hash = {}
all.each do |arr|
arr.each do |elem|
hash[elem[0]] ||= []
hash[elem[0]] << elem[1]
end
end
Array.new hash.map { |k,v| [k, *v]}
For more elems
hash[elem[0]].concat elem[1..-1]
[first,second,third].transpose.map do |array|
array.reduce { |init,e| init << e.last }
end
=> [[111, 1, 4, 7], [222, 2, 5, 8], [333, 3, 6, 9]]
To deal with several last elements in array:
[first,second,third].transpose.map do |array|
array.reduce { |init,e| init + e.drop(1) }
end

Resources