Enable Javascript in your browser and then refresh this page, for a much enhanced experience.
73-liner: K2 solution in Clear category for Lightbulb More by przemyslaw.daniel
from datetime import datetime, timedelta
from collections import defaultdict
from itertools import zip_longest as zipl
from typing import List, Optional, Tuple, Union
MIN_START = datetime(1970, 1, 1, 0, 0, 0)
MAX_END = datetime(9999, 12, 31, 23, 59, 59)
MAX_TIME = 1e9
def split(ranges: List[Tuple[datetime, datetime]],
start: datetime,
stop: datetime) -> List[Tuple[datetime, datetime]]:
""" the idea here is to create non overlapping ranges
for example in case of simple integers
split([(1, 7), (4, 12)], 2, 10) would create
[(2, 4), (4, 7), (7, 10)] """
# find all valid cross sections
cross = sorted(set(sum(ranges, (start, stop))))
cross = filter(lambda x: start <= x <= stop, cross)
# split every range by cross sections
result, cross = [], list(cross)
for left, right in ranges:
values = [c for c in cross if left <= c <= right]
result += list(zip(values, values[1:]))
return result
def limit(timestamps: List[datetime],
end_watching: datetime,
operating: int):
""" combine in periods and limit to operating time """
pairs = timestamps[::2], timestamps[1::2]
for start, stop in zipl(*pairs, fillvalue=end_watching):
if operating <= 0:
break
delta = min(stop - start, timedelta(0, operating))
yield start, start + delta
operating -= (stop - start).total_seconds()
def sum_light(elements: List[Union[datetime, Tuple[datetime, int]]],
start_watching: Optional[datetime]=MIN_START,
end_watching: Optional[datetime]=MAX_END,
operating: Optional[int]=MAX_TIME,
req: Optional[int]=1) -> int:
""" determine how long light is on with multiple bulbs """
# FIX BROKEN TESTS: some injects timedelta instead of int
if isinstance(operating, timedelta):
operating = int(operating.total_seconds())
# first we need to collect datetimes by the index value
separate, result = defaultdict(list), {}
numerate = lambda x: x if type(x) is tuple else (x, 1)
for timestamp, index in map(numerate, elements):
separate[index] += [timestamp]
# combine in pairs and limit operating time for every bulb
for key, value in separate.items():
result[key] = list(limit(value, end_watching, operating))
# split them to have non-overlapping ranges
result = list(result.values())
result = split(sum(result, []), start_watching, end_watching)
# finally calculate differences in seconds when required
return sum((right - left).total_seconds() for left, right in set(result)
if result.count((left, right)) >= req)
Jan. 30, 2021
Comments: