class TimeIntervals::Collection

Constants

ONE_HOUR_IN_SECONDS

Attributes

time_intervals[R]

Public Class Methods

new(time_intervals = []) click to toggle source
# File lib/time_intervals/collection.rb, line 22
def initialize(time_intervals = [])
  @time_intervals = Array(time_intervals).sort
end
wrap(intervals) click to toggle source
# File lib/time_intervals/collection.rb, line 17
def self.wrap(intervals)
  time_intervals = intervals.map { |interval| Interval.new(interval.started_at, interval.ended_at) }
  new(time_intervals)
end

Public Instance Methods

==(other) click to toggle source
# File lib/time_intervals/collection.rb, line 155
def ==(other)
  other.class == self.class && other.time_intervals == time_intervals
end
Also aliased as: eql?
all_intervals_within?(bounding_interval) click to toggle source

Returns true if all of the contained TimeIntervals::Intervals are wholly contained within the bounding interval.

# File lib/time_intervals/collection.rb, line 28
def all_intervals_within?(bounding_interval)
  length_in_seconds == intersect(bounding_interval).length_in_seconds
end
coalesce() click to toggle source

Returns a new coalesced collection of TimeIntervals::Intervals where any that overlap or are adjacent are combined into a single TimeIntervals::Interval.

Given these TimeIntervals::Intervals in the collection:

[——–)

[------)
       [-----)   [-------)
                      [-------)

Calling coalesce returns a TimeIntervals::Collection containing these TimeIntervals::Intervals:

[——————) [————)

# File lib/time_intervals/collection.rb, line 52
def coalesce
  return self if empty?

  coalescing = Interval.create(first)

  result = each_with_object([]) do |current, memo|
    if coalescing.ended_at < current.started_at
      memo << coalescing
      coalescing = Interval.create(current)
    else
      coalescing = Interval.new(
        coalescing.started_at,
        [coalescing.ended_at, current.ended_at].max
      )
    end
  end

  result << Interval.create(coalescing)

  Collection.new(result)
end
eql?(other)
Alias for: ==
has_overlapping_intervals?() click to toggle source

Returns true if any of the contained TimeIntervals::Intervals overlap.

# File lib/time_intervals/collection.rb, line 33
def has_overlapping_intervals?
  length_in_seconds != coalesce.length_in_seconds
end
intersect(intersections) click to toggle source

Returns a new collection of TimeIntervals::Intervals that contains only intersections with the specified intersections: either a nil, a single TimeIntervals::Interval, or a TimeIntervals::Collection.

Note: the intersections are assumed to be disjoint. That is, none of the TimeIntervals::Intervals in intersections overlap.

Given these TimeIntervals::Intervals in the collection:

[——–)

[------)
       [-----)   [-------)
                      [-------)

Calling intersect with these TimeIntervals::Intervals

[--------------) [------)

returns a TimeIntervals::Collection containing these TimeIntervals::Intervals:

[--)
   [-----)   [-) [---)
                  [-----)
# File lib/time_intervals/collection.rb, line 98
def intersect(intersections)
  result = Array(intersections).each_with_object([]) do |intersection, memo|
    memo.concat(intersect_with_time_interval(intersection))
  end

  Collection.new(result)
end
intersect_count(intersections) click to toggle source

Counts the number of TimeIntervals::Intervals that intersect with the given collection of TimeIntervals::Intervals.

Returns a list of [TimeIntervals::Interval, count] tuples. The TimeIntervals::Intervals in the result are the TimeIntervals::Intervals from the argument.

# File lib/time_intervals/collection.rb, line 138
def intersect_count(intersections)
  counts = intersections.map { |slice| intersect_with_time_interval(slice).length }
  intersections.zip(counts)
end
length_in_hours() click to toggle source

The sum of the lengths of the TimeIntervals::Intervals in the collection as hours.

# File lib/time_intervals/collection.rb, line 145
def length_in_hours
  length_in_seconds.to_f / ONE_HOUR_IN_SECONDS
end
length_in_seconds() click to toggle source

The sum of the lengths of the TimeIntervals::Intervals in the collection as seconds.

# File lib/time_intervals/collection.rb, line 151
def length_in_seconds
  time_intervals.reduce(0) { |total, time_intervals| total + time_intervals.length_in_seconds }
end
partition() click to toggle source

Returns a new collection of TimeIntervals::Intervals that are partitions of the original collection.

Given the upper TimeIntervals::Intervals, partition returns the lower TimeIntervals::Intervals:

[——–) [—–) [—) | [——) | | | | | | [—-) | | | | | | | | | | | | | | | | | | | | | [—–) [-) [–) [—) |

[--) [-)  [--)   [---)
# File lib/time_intervals/collection.rb, line 125
def partition
  time_points = @time_intervals.flat_map { |i| [i.started_at, i.ended_at] }.uniq.sort
  start_time_points = time_points[0..-2]
  end_time_points = time_points[1..-1]
  raw_intervals = start_time_points.zip(end_time_points)
  Collection.new(raw_intervals.map { |r| Interval.new(*r) })
end
partition_count() click to toggle source
# File lib/time_intervals/collection.rb, line 106
def partition_count
  partition_intervals = partition

  intersect_count(partition_intervals)
end

Private Instance Methods

intersect_with_time_interval(intersecting) click to toggle source

Returns an array of TimeIntervals::Intervals that contains only intersections with the specified intersecting TimeIntervals::Interval.

Given these TimeIntervals::Intervals in the collection:

[——–)

[------)
       [-----)   [-------)
                      [-------)

Calling intersect_with_time_interval with this TimeIntervals::Interval

[---------------)

returns this array of TimeIntervals::Intervals:

[--)
   [-----)   [--)
# File lib/time_intervals/collection.rb, line 181
def intersect_with_time_interval(intersecting)
  each_with_object([]) do |current, memo|
    next unless intersecting.overlaps?(current)

    memo << Interval.new(
      [intersecting.started_at, current.started_at].max,
      [intersecting.ended_at, current.ended_at].min
    )
  end
end