Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Array of indexes to array of ranges

Tags:

arrays

range

ruby

Ranges in ruby are pretty cool. I end up with arrays such as this:

geneRanges = [(234..25), (500..510), (1640..1653)]

And subsequently have to remove bits of them. For that I:

genePositions = geneRanges.collect {|range| range.entries }.flatten
=> [500, 501, 502, 503, 504, 505, 506, 507, 508, 509, 510, 1640, 1641, 1642, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653]

They get manipulated, so some numbers get excluded, and others may be added. I may end up with this:

[505, 506, 507, 600, 601, 602, 603, 1643, 1644, 1645, 1646, 1647, 1648, 1649, 1650, 1651, 1652, 1653, 1654]

How can I convert this back into a compact array of ranges? It seems that the inverse function should exist? I would expect it to return something like this:

[(505..507), (600..603), (1643..1654)]

Thanks!

like image 384
Yannick Wurm Avatar asked Sep 16 '10 16:09

Yannick Wurm


People also ask

What is the range of an array index?

The elements of array InputData are INTEGERs and the indices are in the range of 0 and 100.

What is array index in array?

The index indicates the position of the element within the array (starting from 1) and is either a number or a field containing a number. The following rules apply when you specify an array name and index: The array name must be a unique symbolic name.

What is array indexing method?

Array Indexing means searching for elements in an array using the index (position) of elements for quick retrieval of information.

How do you select a specific range of an array in Python?

To select an element from Numpy Array , we can use [] operator i.e. It will return the element at given index only.


2 Answers

(New and improved. Stays fresh in your refrigerator for up to two weeks!):

a = [1, 2, 3, 10, 11, 20, 20, 4]

ranges = a.sort.uniq.inject([]) do |spans, n|
  if spans.empty? || spans.last.last != n - 1
    spans + [n..n]
  else
    spans[0..-2] + [spans.last.first..n]
  end
end

p ranges    # [1..4, 10..11, 20..20]
like image 169
Wayne Conrad Avatar answered Sep 23 '22 17:09

Wayne Conrad


Functional, not-very-readable solution:

(a[0,1]+a.each_cons(2).reject{|i,j| j-i==1}.flatten+a[-1,1]).
  each_slice(2).map{|i,j| i..j}

And a nice one:

class Array
  # splits array to sub-arrays wherever two adjacent elements satisfy a condition
  def split_by
    each_cons(2).inject([[first]]){|a, (i, j)|
      a.push([]) if yield(i, j)
      a.last.push j
      a
    }
  end

  # uses split_by to split array to subarrays with consecutive elements, then convert to range
  def to_range
    split_by{|i,j| j-i!=1}.map{|a| a.first..a.last}
  end
end

[505, 506, 507, 600, 1647, 1648, 1649, 1650, 1651, 1654].split_by{|i,j| j-i!=1}
#=> [[505, 506, 507], [600], [1647, 1648, 1649, 1650, 1651], [1654]]
[505, 506, 507, 600, 1647, 1648, 1649, 1650, 1651, 1654].to_range
#=> [505..507, 600..600, 1647..1651, 1654..1654]
like image 30
Mladen Jablanović Avatar answered Sep 23 '22 17:09

Mladen Jablanović