def find_multiples_of_4_and_6(n): for i in range(n): if not (i % 4 and i % 6): yield i
find_multiples_of_4_and_6 = lambda n : [i for i in range(n) if not (i % 4 and i % 6)]- def find_multiples_of_4_and_6(n):
- for i in range(n):
- if not (i % 4 and i % 6):
- yield i
import codewars_test as test import solution # or from solution import example @test.describe("Example") def test_group(): @test.it("test case") def test_case(): test.assert_equals(list(find_multiples_of_4_and_6(20)), [0, 4, 6, 8, 12, 16, 18]) test.assert_equals(list(find_multiples_of_4_and_6(35)), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32]) test.assert_equals(list(find_multiples_of_4_and_6(120)), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, 48, 52, 54, 56, 60, 64, 66, 68, 72, 76, 78, 80, 84, 88, 90, 92, 96, 100, 102, 104, 108, 112, 114, 116])
- import codewars_test as test
- import solution # or from solution import example
- @test.describe("Example")
- def test_group():
- @test.it("test case")
- def test_case():
test.assert_equals(find_multiples_of_4_and_6(20), [0, 4, 6, 8, 12, 16, 18])test.assert_equals(find_multiples_of_4_and_6(35), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32])test.assert_equals(find_multiples_of_4_and_6(120), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, 48, 52, 54, 56, 60, 64, 66, 68, 72, 76, 78, 80, 84, 88, 90, 92, 96, 100, 102, 104, 108, 112, 114, 116])- test.assert_equals(list(find_multiples_of_4_and_6(20)), [0, 4, 6, 8, 12, 16, 18])
- test.assert_equals(list(find_multiples_of_4_and_6(35)), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32])
- test.assert_equals(list(find_multiples_of_4_and_6(120)), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, 48, 52, 54, 56, 60, 64, 66, 68, 72, 76, 78, 80, 84, 88, 90, 92, 96, 100, 102, 104, 108, 112, 114, 116])
Form follows function
def find_multiples_of_4_and_6(n): return [i for i in range(n) if not (i % 4 and i % 6)]
def find_multiples(n):return sum(0 if i % 4 and i % 6 else i for i in range(n))- def find_multiples_of_4_and_6(n):
- return [i for i in range(n) if not (i % 4 and i % 6)]
import codewars_test as test import solution # or from solution import example @test.describe("Example") def test_group(): @test.it("test case") def test_case(): test.assert_equals(find_multiples_of_4_and_6(20), [0, 4, 6, 8, 12, 16, 18]) test.assert_equals(find_multiples_of_4_and_6(35), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32]) test.assert_equals(find_multiples_of_4_and_6(120), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, 48, 52, 54, 56, 60, 64, 66, 68, 72, 76, 78, 80, 84, 88, 90, 92, 96, 100, 102, 104, 108, 112, 114, 116])
- import codewars_test as test
- import solution # or from solution import example
- @test.describe("Example")
- def test_group():
- @test.it("test case")
- def test_case():
test.assert_equals(find_multiples(20), 64)test.assert_equals(find_multiples(35), 198)test.assert_equals(find_multiples(120), 2340)- test.assert_equals(find_multiples_of_4_and_6(20), [0, 4, 6, 8, 12, 16, 18])
- test.assert_equals(find_multiples_of_4_and_6(35), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32])
- test.assert_equals(find_multiples_of_4_and_6(120), [0, 4, 6, 8, 12, 16, 18, 20, 24, 28, 30, 32, 36, 40, 42, 44, 48, 52, 54, 56, 60, 64, 66, 68, 72, 76, 78, 80, 84, 88, 90, 92, 96, 100, 102, 104, 108, 112, 114, 116])
"Don't bore us, get to the chorus" - Dave Grohl (Flipped the ternary)
"Drop that Zero" - Denise Lasalle (Truth implied by presence)
Refactored the data-gen methods to use some lambdas and a Datastruct that should make the whole block easier to read and easier to update/extend later.
Refactored calculate_speeds
. Instead of re-iterating through the list v
4 times for each loop around data.items()
, we loop just once, by appending to t
and then using an 'Accumulator' class (Diff
), we can achieve the same results and make it easier to read.
The Diff
class extends list
and overrides the append
method, 'shifting' a
and b
each time a new value is added and appending the difference instead.
Could go further with additional dataclasses for speed and position, but I felt liket his hit a nice 'balance' of readability and effort.
"""Test equipment positions.""" from __future__ import annotations import datetime as dt import functools import random from collections import defaultdict from dataclasses import dataclass from typing import Any from unittest import TestCase @dataclass class Position: """3D Coordinate data class""" x: float y: float z: float # Helper λ for building consistent datetime objects since we're constraining everything to the same day/hour/minute to_datetime = lambda s: dt.datetime(2022, 1, 1, hour=12, minute=0, second=s) def data_dict() -> defaultdict[str, Any]: """Return all equipment positions.""" d = defaultdict(list) # λ for ease when updating or adding new mocked data add_data = lambda k, s, p: d[k].append({'position': p, 'timestamp': to_datetime(s)}) add_data('T123', 0, Position(42, 24, 0.42)) add_data('T456', 0, Position(21.0, 34, 0.289)) add_data('T789', 0, Position(17, 39, 0.789)) add_data('T456', 3, Position(91.0, 114, 0.489)) add_data('T123', 1, Position(43, 25, 0.43)) add_data('T789', 6, Position(19., 79, 0.991)) add_data('T123', 2, Position(46, 29, 0.44)) add_data('T456', 4, Position(24.0, 37, 0.297)) add_data('T123', 3, Position(49.0, 32, 0.451)) add_data('T789', 7, Position(23., 81, 1.103)) return d def latest_snapshot() -> dict[str, Any]: """Return a snapshot of latest equipment.""" return { 'T123': {'position': Position(49.0, 32, 0.451), 'timestamp': to_datetime(3)}, 'T456': {'position': Position(24.0, 37, 0.297), 'timestamp': to_datetime(4)}, 'T789': {'position': Position(23.0, 81, 1.103), 'timestamp': to_datetime(7)}, } def counts() -> dict[str, int]: """Return counts per equipment.""" return { 'T123': 4, 'T456': 3, 'T789': 3 } def speeds() -> defaultdict[str, Any]: """Return speeds of equipment.""" d = defaultdict(list) # λ for updating or adding new mocked data add_speed = lambda k, t, s: d[k].append({'speed': s, 'timestamp': to_datetime(t)}) add_speed('T123', 3, round(4.242654947082074, 1)) add_speed('T123', 2, round(5.00000999999, 1)) add_speed('T123', 1, round(1.4142489172702237, 1)) add_speed('T123', 0, None) add_speed('T456', 4, round(102.0687849638664, 1)) add_speed('T456', 3, round(35.43388209045123, 1)) add_speed('T456', 0, None) add_speed('T789', 7, round(4.473538196997986, 1)) add_speed('T789', 6, round(6.6750796998987205, 1)) add_speed('T789', 0, None) return d def find_furthest_west(data: defaultdict) -> str: """ Find the name of the truck that is furthest west. That is, the truck with the smallest easting position component. """ f = lambda v: min([x['position'].x for x in v]) return functools.reduce(lambda a, b: a if f(data[a]) < f(data[b]) else b, data.keys()) def get_latest_snapshot(data: defaultdict) -> dict[str, Any]: """ Return a snapshot of the latest positional updates for the equipment. """ return {k: v[-1] for k, v in data.items()} def get_counts(data: defaultdict) -> dict[str, int]: """Return a dict of trucks and the times they have provided updates.""" return {k: len(v) for k, v in data.items()} class Diff(list): """A utility class that creates a list of the differences between consecutive pair of numbers that are appended""" a = b = None def append(self, c): self.a, self.b = self.b, c if self.a is not None: super().append(self.b - self.a) def calculate_speeds(data: defaultdict) -> defaultdict[str, Any]: """Return a dict of equipment and the speeds they are travelling at.""" out = defaultdict(list) for k, v in data.items(): t = [] dt = Diff() dx = Diff() dy = Diff() for x in v: t.append(x['timestamp']) dt.append(x["timestamp"].second) dx.append(x["position"].x) dy.append(x["position"].y) dd = [(a ** 2 + b ** 2) ** 0.5 for a, b in zip(dx, dy)] s = [a / b for a, b in zip(dd, dt)] for speed, timestamp in zip(s[::-1], t[::-1]): out[k].append({'speed': round(speed, 1), 'timestamp': timestamp}) out[k].append({'speed': None, 'timestamp': t[0]}) return out
- """Test equipment positions."""
- from __future__ import annotations
from collections import defaultdict- import datetime as dt
- import functools
- import random
- from collections import defaultdict
- from dataclasses import dataclass
- from typing import Any
- from unittest import TestCase
- @dataclass
- class Position:
- """3D Coordinate data class"""
- x: float
- y: float
- z: float
- # Helper λ for building consistent datetime objects since we're constraining everything to the same day/hour/minute
- to_datetime = lambda s: dt.datetime(2022, 1, 1, hour=12, minute=0, second=s)
- def data_dict() -> defaultdict[str, Any]:
- """Return all equipment positions."""
- d = defaultdict(list)
d['T123'].append({'position': {'x': 42, 'y': 24, 'z': 0.42}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=0)})d['T456'].append({'position': {'x': 21.0, 'y': 34, 'z': 0.289}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=0)})d['T789'].append({'position': {'x': 17, 'y': 39, 'z': 0.789}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=0)})d['T456'].append({'position': {'x': 91.0, 'y': 114, 'z': 0.489}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=3)})d['T123'].append({'position': {'x': 43, 'y': 25, 'z': 0.43}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=1)})d['T789'].append({'position': {'x': 19., 'y': 79, 'z': 0.991}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=6)})d['T123'].append({'position': {'x': 46, 'y': 29, 'z': 0.44}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=2)})d['T456'].append({'position': {'x': 24.0, 'y': 37, 'z': 0.297}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=4)})d['T123'].append({'position': {'x': 49.0, 'y': 32, 'z': 0.451}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=3)})d['T789'].append({'position': {'x': 23., 'y': 81, 'z': 1.103}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=7)})- # λ for ease when updating or adding new mocked data
- add_data = lambda k, s, p: d[k].append({'position': p, 'timestamp': to_datetime(s)})
- add_data('T123', 0, Position(42, 24, 0.42))
- add_data('T456', 0, Position(21.0, 34, 0.289))
- add_data('T789', 0, Position(17, 39, 0.789))
- add_data('T456', 3, Position(91.0, 114, 0.489))
- add_data('T123', 1, Position(43, 25, 0.43))
- add_data('T789', 6, Position(19., 79, 0.991))
- add_data('T123', 2, Position(46, 29, 0.44))
- add_data('T456', 4, Position(24.0, 37, 0.297))
- add_data('T123', 3, Position(49.0, 32, 0.451))
- add_data('T789', 7, Position(23., 81, 1.103))
- return d
- def latest_snapshot() -> dict[str, Any]:
- """Return a snapshot of latest equipment."""
- return {
'T123': {'position': {'x': 49.0, 'y': 32, 'z': 0.451}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=3)},'T456': {'position': {'x': 24.0, 'y': 37, 'z': 0.297}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=4)},'T789': {'position': {'x': 23.0, 'y': 81, 'z': 1.103}, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=7)},- 'T123': {'position': Position(49.0, 32, 0.451), 'timestamp': to_datetime(3)},
- 'T456': {'position': Position(24.0, 37, 0.297), 'timestamp': to_datetime(4)},
- 'T789': {'position': Position(23.0, 81, 1.103), 'timestamp': to_datetime(7)},
- }
- def counts() -> dict[str, int]:
- """Return counts per equipment."""
- return {
- 'T123': 4,
- 'T456': 3,
- 'T789': 3
- }
- def speeds() -> defaultdict[str, Any]:
- """Return speeds of equipment."""
- d = defaultdict(list)
d['T123'].append({'speed': round(4.242654947082074, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=3)})d['T123'].append({'speed': round(5.00000999999, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=2)})d['T123'].append({'speed': round(1.4142489172702237, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=1)})d['T123'].append({'speed': None, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=0)})d['T456'].append({'speed': round(102.0687849638664, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=4)})d['T456'].append({'speed': round(35.43388209045123, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=3)})d['T456'].append({'speed': None, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=0)})d['T789'].append({'speed': round(4.473538196997986, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=7)})d['T789'].append({'speed': round(6.6750796998987205, 1), 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=6)})d['T789'].append({'speed': None, 'timestamp': dt.datetime(2022, 1, 1, hour=12, minute=0, second=0)})- # λ for updating or adding new mocked data
- add_speed = lambda k, t, s: d[k].append({'speed': s, 'timestamp': to_datetime(t)})
- add_speed('T123', 3, round(4.242654947082074, 1))
- add_speed('T123', 2, round(5.00000999999, 1))
- add_speed('T123', 1, round(1.4142489172702237, 1))
- add_speed('T123', 0, None)
- add_speed('T456', 4, round(102.0687849638664, 1))
- add_speed('T456', 3, round(35.43388209045123, 1))
- add_speed('T456', 0, None)
- add_speed('T789', 7, round(4.473538196997986, 1))
- add_speed('T789', 6, round(6.6750796998987205, 1))
- add_speed('T789', 0, None)
- return d
- def find_furthest_west(data: defaultdict) -> str:
- """
- Find the name of the truck that is furthest west. That is,
- the truck with the smallest easting position component.
- """
f = lambda v: min([x['position']['x'] for x in v])- f = lambda v: min([x['position'].x for x in v])
- return functools.reduce(lambda a, b: a if f(data[a]) < f(data[b]) else b, data.keys())
- def get_latest_snapshot(data: defaultdict) -> dict[str, Any]:
- """
- Return a snapshot of the latest positional updates for the
- equipment.
- """
- return {k: v[-1] for k, v in data.items()}
- def get_counts(data: defaultdict) -> dict[str, int]:
- """Return a dict of trucks and the times they have provided updates."""
return {k : len(v) for k, v in data.items()}- return {k: len(v) for k, v in data.items()}
- class Diff(list):
- """A utility class that creates a list of the differences between consecutive pair of numbers that are appended"""
- a = b = None
- def append(self, c):
- self.a, self.b = self.b, c
- if self.a is not None:
- super().append(self.b - self.a)
- def calculate_speeds(data: defaultdict) -> defaultdict[str, Any]:
- """Return a dict of equipment and the speeds they are travelling at."""
- out = defaultdict(list)
diff = lambda x: [b - a for a, b in zip(x, x[1:])]- for k, v in data.items():
t = [x['timestamp'] for x in v]dt = diff([x["timestamp"].second for x in v])dx = diff([x["position"]["x"] for x in v])dy = diff([x["position"]["y"] for x in v])- t = []
- dt = Diff()
- dx = Diff()
- dy = Diff()
- for x in v:
- t.append(x['timestamp'])
- dt.append(x["timestamp"].second)
- dx.append(x["position"].x)
- dy.append(x["position"].y)
- dd = [(a ** 2 + b ** 2) ** 0.5 for a, b in zip(dx, dy)]
- s = [a / b for a, b in zip(dd, dt)]
- for speed, timestamp in zip(s[::-1], t[::-1]):
- out[k].append({'speed': round(speed, 1), 'timestamp': timestamp})
- out[k].append({'speed': None, 'timestamp': t[0]})
return out- return out