Advent of Code 2018 Day 4 - Pythonic Sleepy Guards












4














Introduction



I'm using Advent of Code 2018 to learn Python better, interested in the new type support for Python 3.7, I decided to go with that.



Here is my solution to Advent of Code Day 4, both part 1 and 2. I'm returning the answers for both part 1 and 2 as a tuple.



Problem Description



The problem essentially boils down to: Given a timestamped unsorted list of events of guards beginning their shift, waking up and falling asleep, determine the following:



Part 1: Which guard is asleep the most and on which minute is that guard mostly asleep? Return guard id multiplied by the minute number.



Part 2: Which guard is most frequently asleep on the same minute? Again, return guard id multiplied by the minute number.



For full problem description, please see Advent of Code Day 4



Concerns



I'm a big fan of Java 8 Stream API and C# Linq, I kind of expected Python to be more like that. I'm not sure if the nested function calls like sorted(list(...)) or len(list(...)) are "Pythonic". Likewise, it feels like I should be able to use some reducer-like function calls instead of imperatively looping through stuff to find the most common sleeper of some kind. Or is the way I have written this code the Python way to do it?



Code



from dataclasses import dataclass
from datetime import datetime
from days import read_file
from enum import Enum
from collections import defaultdict, namedtuple
from statistics import mode
import statistics
import operator
import re


class EventType(Enum):
STARTS_SHIFT = 1
FALLS_ASLEEP = 2
WAKES_UP = 3


@dataclass
class Event:
time: datetime
guard: int
event: EventType


@dataclass
class GuardSleep:
sleep_total: int
last_sleep: int
sleeps: list

def add_sleeps(self, minute):
for i in range(self.last_sleep, minute):
self.sleeps.append(i)


def get_guard(line: str):
if "Guard" in line:
guard_id = re.search("Guard #(\d+)", line)
return int(guard_id.group(1))
return -1


def event_type(line):
if "begins shift" in line:
return EventType.STARTS_SHIFT
if "falls asleep" in line:
return EventType.FALLS_ASLEEP
if "wakes up" in line:
return EventType.WAKES_UP
raise Exception("Unknown line: " + line)


def day4() -> (int, int):
events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))

guard = -1
guardsleep = defaultdict(lambda: GuardSleep(0, 0, ))
for event in events:
if event.guard >= 0:
guard = event.guard

if event.event == EventType.FALLS_ASLEEP:
guardsleep[guard].last_sleep = event.time.minute

if event.event == EventType.WAKES_UP:
guardsleep[guard].sleep_total += event.time.minute - guardsleep[guard].last_sleep
guardsleep[guard].add_sleeps(event.time.minute)

most_sleepy_guard_number = max(guardsleep, key=(lambda key: guardsleep[key].sleep_total))

most_sleepy_guard = guardsleep[most_sleepy_guard_number]
part1_result = most_sleepy_guard_number * mode(sorted(most_sleepy_guard.sleeps))

# Part 2
MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])
most_sleepy = MostSleepy(0, 0, 0)
for k in guardsleep:
current_guard = guardsleep[k]
try:
most_common_minute = mode(sorted(current_guard.sleeps))
amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))
if amount > most_sleepy.amount:
most_sleepy = MostSleepy(k, most_common_minute, amount)
except statistics.StatisticsError:
print("No unique most common minute for " + str(k))

return part1_result, most_sleepy.id * most_sleepy.minute


if __name__ == '__main__':
print(day4())









share|improve this question



























    4














    Introduction



    I'm using Advent of Code 2018 to learn Python better, interested in the new type support for Python 3.7, I decided to go with that.



    Here is my solution to Advent of Code Day 4, both part 1 and 2. I'm returning the answers for both part 1 and 2 as a tuple.



    Problem Description



    The problem essentially boils down to: Given a timestamped unsorted list of events of guards beginning their shift, waking up and falling asleep, determine the following:



    Part 1: Which guard is asleep the most and on which minute is that guard mostly asleep? Return guard id multiplied by the minute number.



    Part 2: Which guard is most frequently asleep on the same minute? Again, return guard id multiplied by the minute number.



    For full problem description, please see Advent of Code Day 4



    Concerns



    I'm a big fan of Java 8 Stream API and C# Linq, I kind of expected Python to be more like that. I'm not sure if the nested function calls like sorted(list(...)) or len(list(...)) are "Pythonic". Likewise, it feels like I should be able to use some reducer-like function calls instead of imperatively looping through stuff to find the most common sleeper of some kind. Or is the way I have written this code the Python way to do it?



    Code



    from dataclasses import dataclass
    from datetime import datetime
    from days import read_file
    from enum import Enum
    from collections import defaultdict, namedtuple
    from statistics import mode
    import statistics
    import operator
    import re


    class EventType(Enum):
    STARTS_SHIFT = 1
    FALLS_ASLEEP = 2
    WAKES_UP = 3


    @dataclass
    class Event:
    time: datetime
    guard: int
    event: EventType


    @dataclass
    class GuardSleep:
    sleep_total: int
    last_sleep: int
    sleeps: list

    def add_sleeps(self, minute):
    for i in range(self.last_sleep, minute):
    self.sleeps.append(i)


    def get_guard(line: str):
    if "Guard" in line:
    guard_id = re.search("Guard #(\d+)", line)
    return int(guard_id.group(1))
    return -1


    def event_type(line):
    if "begins shift" in line:
    return EventType.STARTS_SHIFT
    if "falls asleep" in line:
    return EventType.FALLS_ASLEEP
    if "wakes up" in line:
    return EventType.WAKES_UP
    raise Exception("Unknown line: " + line)


    def day4() -> (int, int):
    events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))

    guard = -1
    guardsleep = defaultdict(lambda: GuardSleep(0, 0, ))
    for event in events:
    if event.guard >= 0:
    guard = event.guard

    if event.event == EventType.FALLS_ASLEEP:
    guardsleep[guard].last_sleep = event.time.minute

    if event.event == EventType.WAKES_UP:
    guardsleep[guard].sleep_total += event.time.minute - guardsleep[guard].last_sleep
    guardsleep[guard].add_sleeps(event.time.minute)

    most_sleepy_guard_number = max(guardsleep, key=(lambda key: guardsleep[key].sleep_total))

    most_sleepy_guard = guardsleep[most_sleepy_guard_number]
    part1_result = most_sleepy_guard_number * mode(sorted(most_sleepy_guard.sleeps))

    # Part 2
    MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])
    most_sleepy = MostSleepy(0, 0, 0)
    for k in guardsleep:
    current_guard = guardsleep[k]
    try:
    most_common_minute = mode(sorted(current_guard.sleeps))
    amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))
    if amount > most_sleepy.amount:
    most_sleepy = MostSleepy(k, most_common_minute, amount)
    except statistics.StatisticsError:
    print("No unique most common minute for " + str(k))

    return part1_result, most_sleepy.id * most_sleepy.minute


    if __name__ == '__main__':
    print(day4())









    share|improve this question

























      4












      4








      4







      Introduction



      I'm using Advent of Code 2018 to learn Python better, interested in the new type support for Python 3.7, I decided to go with that.



      Here is my solution to Advent of Code Day 4, both part 1 and 2. I'm returning the answers for both part 1 and 2 as a tuple.



      Problem Description



      The problem essentially boils down to: Given a timestamped unsorted list of events of guards beginning their shift, waking up and falling asleep, determine the following:



      Part 1: Which guard is asleep the most and on which minute is that guard mostly asleep? Return guard id multiplied by the minute number.



      Part 2: Which guard is most frequently asleep on the same minute? Again, return guard id multiplied by the minute number.



      For full problem description, please see Advent of Code Day 4



      Concerns



      I'm a big fan of Java 8 Stream API and C# Linq, I kind of expected Python to be more like that. I'm not sure if the nested function calls like sorted(list(...)) or len(list(...)) are "Pythonic". Likewise, it feels like I should be able to use some reducer-like function calls instead of imperatively looping through stuff to find the most common sleeper of some kind. Or is the way I have written this code the Python way to do it?



      Code



      from dataclasses import dataclass
      from datetime import datetime
      from days import read_file
      from enum import Enum
      from collections import defaultdict, namedtuple
      from statistics import mode
      import statistics
      import operator
      import re


      class EventType(Enum):
      STARTS_SHIFT = 1
      FALLS_ASLEEP = 2
      WAKES_UP = 3


      @dataclass
      class Event:
      time: datetime
      guard: int
      event: EventType


      @dataclass
      class GuardSleep:
      sleep_total: int
      last_sleep: int
      sleeps: list

      def add_sleeps(self, minute):
      for i in range(self.last_sleep, minute):
      self.sleeps.append(i)


      def get_guard(line: str):
      if "Guard" in line:
      guard_id = re.search("Guard #(\d+)", line)
      return int(guard_id.group(1))
      return -1


      def event_type(line):
      if "begins shift" in line:
      return EventType.STARTS_SHIFT
      if "falls asleep" in line:
      return EventType.FALLS_ASLEEP
      if "wakes up" in line:
      return EventType.WAKES_UP
      raise Exception("Unknown line: " + line)


      def day4() -> (int, int):
      events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))

      guard = -1
      guardsleep = defaultdict(lambda: GuardSleep(0, 0, ))
      for event in events:
      if event.guard >= 0:
      guard = event.guard

      if event.event == EventType.FALLS_ASLEEP:
      guardsleep[guard].last_sleep = event.time.minute

      if event.event == EventType.WAKES_UP:
      guardsleep[guard].sleep_total += event.time.minute - guardsleep[guard].last_sleep
      guardsleep[guard].add_sleeps(event.time.minute)

      most_sleepy_guard_number = max(guardsleep, key=(lambda key: guardsleep[key].sleep_total))

      most_sleepy_guard = guardsleep[most_sleepy_guard_number]
      part1_result = most_sleepy_guard_number * mode(sorted(most_sleepy_guard.sleeps))

      # Part 2
      MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])
      most_sleepy = MostSleepy(0, 0, 0)
      for k in guardsleep:
      current_guard = guardsleep[k]
      try:
      most_common_minute = mode(sorted(current_guard.sleeps))
      amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))
      if amount > most_sleepy.amount:
      most_sleepy = MostSleepy(k, most_common_minute, amount)
      except statistics.StatisticsError:
      print("No unique most common minute for " + str(k))

      return part1_result, most_sleepy.id * most_sleepy.minute


      if __name__ == '__main__':
      print(day4())









      share|improve this question













      Introduction



      I'm using Advent of Code 2018 to learn Python better, interested in the new type support for Python 3.7, I decided to go with that.



      Here is my solution to Advent of Code Day 4, both part 1 and 2. I'm returning the answers for both part 1 and 2 as a tuple.



      Problem Description



      The problem essentially boils down to: Given a timestamped unsorted list of events of guards beginning their shift, waking up and falling asleep, determine the following:



      Part 1: Which guard is asleep the most and on which minute is that guard mostly asleep? Return guard id multiplied by the minute number.



      Part 2: Which guard is most frequently asleep on the same minute? Again, return guard id multiplied by the minute number.



      For full problem description, please see Advent of Code Day 4



      Concerns



      I'm a big fan of Java 8 Stream API and C# Linq, I kind of expected Python to be more like that. I'm not sure if the nested function calls like sorted(list(...)) or len(list(...)) are "Pythonic". Likewise, it feels like I should be able to use some reducer-like function calls instead of imperatively looping through stuff to find the most common sleeper of some kind. Or is the way I have written this code the Python way to do it?



      Code



      from dataclasses import dataclass
      from datetime import datetime
      from days import read_file
      from enum import Enum
      from collections import defaultdict, namedtuple
      from statistics import mode
      import statistics
      import operator
      import re


      class EventType(Enum):
      STARTS_SHIFT = 1
      FALLS_ASLEEP = 2
      WAKES_UP = 3


      @dataclass
      class Event:
      time: datetime
      guard: int
      event: EventType


      @dataclass
      class GuardSleep:
      sleep_total: int
      last_sleep: int
      sleeps: list

      def add_sleeps(self, minute):
      for i in range(self.last_sleep, minute):
      self.sleeps.append(i)


      def get_guard(line: str):
      if "Guard" in line:
      guard_id = re.search("Guard #(\d+)", line)
      return int(guard_id.group(1))
      return -1


      def event_type(line):
      if "begins shift" in line:
      return EventType.STARTS_SHIFT
      if "falls asleep" in line:
      return EventType.FALLS_ASLEEP
      if "wakes up" in line:
      return EventType.WAKES_UP
      raise Exception("Unknown line: " + line)


      def day4() -> (int, int):
      events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))

      guard = -1
      guardsleep = defaultdict(lambda: GuardSleep(0, 0, ))
      for event in events:
      if event.guard >= 0:
      guard = event.guard

      if event.event == EventType.FALLS_ASLEEP:
      guardsleep[guard].last_sleep = event.time.minute

      if event.event == EventType.WAKES_UP:
      guardsleep[guard].sleep_total += event.time.minute - guardsleep[guard].last_sleep
      guardsleep[guard].add_sleeps(event.time.minute)

      most_sleepy_guard_number = max(guardsleep, key=(lambda key: guardsleep[key].sleep_total))

      most_sleepy_guard = guardsleep[most_sleepy_guard_number]
      part1_result = most_sleepy_guard_number * mode(sorted(most_sleepy_guard.sleeps))

      # Part 2
      MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])
      most_sleepy = MostSleepy(0, 0, 0)
      for k in guardsleep:
      current_guard = guardsleep[k]
      try:
      most_common_minute = mode(sorted(current_guard.sleeps))
      amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))
      if amount > most_sleepy.amount:
      most_sleepy = MostSleepy(k, most_common_minute, amount)
      except statistics.StatisticsError:
      print("No unique most common minute for " + str(k))

      return part1_result, most_sleepy.id * most_sleepy.minute


      if __name__ == '__main__':
      print(day4())






      python python-3.x programming-challenge






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked yesterday









      Simon Forsberg

      48.5k7129286




      48.5k7129286






















          1 Answer
          1






          active

          oldest

          votes


















          3














          Replacing chained if with dictionary lookup



          This function:



          def event_type(line):
          if "begins shift" in line:
          return EventType.STARTS_SHIFT
          if "falls asleep" in line:
          return EventType.FALLS_ASLEEP
          if "wakes up" in line:
          return EventType.WAKES_UP
          raise Exception("Unknown line: " + line)


          isn't bad, but chained if like this smell. It may be better represented as a dictionary, where the key is the string above, the value is the enum value, and you do a simple key lookup based on the last two words of every line. Whereas chained if is worst-case O(n), dictionary lookup is O(1). Then - no ifs needed, and you get the exception for free if key lookup fails.



          Use raw strings



          re.search("Guard #(\d+)", line)


          should be



          re.search(r"Guard #(d+)", line)


          Settle down with the one-liners



          This:



          events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))


          is effectively illegible. Break this up into multiple lines - including a temporary variable for the strptime, as well as linebreaks in the list comprehension itself.



          Don't use lists if you can use tuples



          This:



          MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])


          should be



          MostSleepy = namedtuple('MostCommon', ('id', 'minute', 'amount'))


          for various reasons - tuples are immutable, so use them for immutable data; and under certain narrow contexts (certainly not this one) they're faster.



          Use a sum instead of a list constructor



          This:



          amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))


          should be



          amount = sum(1 for m in current_guard.sleeps if m == most_common_minute)


          (Also, even if you kept using len, you should use a tuple constructor instead of a list constructor.)



          Another footnote - don't put inner parens in expressions like list((...generator...)). Constructors can accept generator expressions directly.






          share|improve this answer























            Your Answer





            StackExchange.ifUsing("editor", function () {
            return StackExchange.using("mathjaxEditing", function () {
            StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
            StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
            });
            });
            }, "mathjax-editing");

            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "196"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: false,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: null,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210869%2fadvent-of-code-2018-day-4-pythonic-sleepy-guards%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            3














            Replacing chained if with dictionary lookup



            This function:



            def event_type(line):
            if "begins shift" in line:
            return EventType.STARTS_SHIFT
            if "falls asleep" in line:
            return EventType.FALLS_ASLEEP
            if "wakes up" in line:
            return EventType.WAKES_UP
            raise Exception("Unknown line: " + line)


            isn't bad, but chained if like this smell. It may be better represented as a dictionary, where the key is the string above, the value is the enum value, and you do a simple key lookup based on the last two words of every line. Whereas chained if is worst-case O(n), dictionary lookup is O(1). Then - no ifs needed, and you get the exception for free if key lookup fails.



            Use raw strings



            re.search("Guard #(\d+)", line)


            should be



            re.search(r"Guard #(d+)", line)


            Settle down with the one-liners



            This:



            events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))


            is effectively illegible. Break this up into multiple lines - including a temporary variable for the strptime, as well as linebreaks in the list comprehension itself.



            Don't use lists if you can use tuples



            This:



            MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])


            should be



            MostSleepy = namedtuple('MostCommon', ('id', 'minute', 'amount'))


            for various reasons - tuples are immutable, so use them for immutable data; and under certain narrow contexts (certainly not this one) they're faster.



            Use a sum instead of a list constructor



            This:



            amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))


            should be



            amount = sum(1 for m in current_guard.sleeps if m == most_common_minute)


            (Also, even if you kept using len, you should use a tuple constructor instead of a list constructor.)



            Another footnote - don't put inner parens in expressions like list((...generator...)). Constructors can accept generator expressions directly.






            share|improve this answer




























              3














              Replacing chained if with dictionary lookup



              This function:



              def event_type(line):
              if "begins shift" in line:
              return EventType.STARTS_SHIFT
              if "falls asleep" in line:
              return EventType.FALLS_ASLEEP
              if "wakes up" in line:
              return EventType.WAKES_UP
              raise Exception("Unknown line: " + line)


              isn't bad, but chained if like this smell. It may be better represented as a dictionary, where the key is the string above, the value is the enum value, and you do a simple key lookup based on the last two words of every line. Whereas chained if is worst-case O(n), dictionary lookup is O(1). Then - no ifs needed, and you get the exception for free if key lookup fails.



              Use raw strings



              re.search("Guard #(\d+)", line)


              should be



              re.search(r"Guard #(d+)", line)


              Settle down with the one-liners



              This:



              events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))


              is effectively illegible. Break this up into multiple lines - including a temporary variable for the strptime, as well as linebreaks in the list comprehension itself.



              Don't use lists if you can use tuples



              This:



              MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])


              should be



              MostSleepy = namedtuple('MostCommon', ('id', 'minute', 'amount'))


              for various reasons - tuples are immutable, so use them for immutable data; and under certain narrow contexts (certainly not this one) they're faster.



              Use a sum instead of a list constructor



              This:



              amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))


              should be



              amount = sum(1 for m in current_guard.sleeps if m == most_common_minute)


              (Also, even if you kept using len, you should use a tuple constructor instead of a list constructor.)



              Another footnote - don't put inner parens in expressions like list((...generator...)). Constructors can accept generator expressions directly.






              share|improve this answer


























                3












                3








                3






                Replacing chained if with dictionary lookup



                This function:



                def event_type(line):
                if "begins shift" in line:
                return EventType.STARTS_SHIFT
                if "falls asleep" in line:
                return EventType.FALLS_ASLEEP
                if "wakes up" in line:
                return EventType.WAKES_UP
                raise Exception("Unknown line: " + line)


                isn't bad, but chained if like this smell. It may be better represented as a dictionary, where the key is the string above, the value is the enum value, and you do a simple key lookup based on the last two words of every line. Whereas chained if is worst-case O(n), dictionary lookup is O(1). Then - no ifs needed, and you get the exception for free if key lookup fails.



                Use raw strings



                re.search("Guard #(\d+)", line)


                should be



                re.search(r"Guard #(d+)", line)


                Settle down with the one-liners



                This:



                events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))


                is effectively illegible. Break this up into multiple lines - including a temporary variable for the strptime, as well as linebreaks in the list comprehension itself.



                Don't use lists if you can use tuples



                This:



                MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])


                should be



                MostSleepy = namedtuple('MostCommon', ('id', 'minute', 'amount'))


                for various reasons - tuples are immutable, so use them for immutable data; and under certain narrow contexts (certainly not this one) they're faster.



                Use a sum instead of a list constructor



                This:



                amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))


                should be



                amount = sum(1 for m in current_guard.sleeps if m == most_common_minute)


                (Also, even if you kept using len, you should use a tuple constructor instead of a list constructor.)



                Another footnote - don't put inner parens in expressions like list((...generator...)). Constructors can accept generator expressions directly.






                share|improve this answer














                Replacing chained if with dictionary lookup



                This function:



                def event_type(line):
                if "begins shift" in line:
                return EventType.STARTS_SHIFT
                if "falls asleep" in line:
                return EventType.FALLS_ASLEEP
                if "wakes up" in line:
                return EventType.WAKES_UP
                raise Exception("Unknown line: " + line)


                isn't bad, but chained if like this smell. It may be better represented as a dictionary, where the key is the string above, the value is the enum value, and you do a simple key lookup based on the last two words of every line. Whereas chained if is worst-case O(n), dictionary lookup is O(1). Then - no ifs needed, and you get the exception for free if key lookup fails.



                Use raw strings



                re.search("Guard #(\d+)", line)


                should be



                re.search(r"Guard #(d+)", line)


                Settle down with the one-liners



                This:



                events = sorted(list(Event(datetime.strptime(line[1:17], "%Y-%m-%d %H:%M"), get_guard(line), event_type(line)) for line in read_file(4)), key=operator.attrgetter("time"))


                is effectively illegible. Break this up into multiple lines - including a temporary variable for the strptime, as well as linebreaks in the list comprehension itself.



                Don't use lists if you can use tuples



                This:



                MostSleepy = namedtuple('MostCommon', ['id', 'minute', 'amount'])


                should be



                MostSleepy = namedtuple('MostCommon', ('id', 'minute', 'amount'))


                for various reasons - tuples are immutable, so use them for immutable data; and under certain narrow contexts (certainly not this one) they're faster.



                Use a sum instead of a list constructor



                This:



                amount = len(list((m for m in current_guard.sleeps if m == most_common_minute)))


                should be



                amount = sum(1 for m in current_guard.sleeps if m == most_common_minute)


                (Also, even if you kept using len, you should use a tuple constructor instead of a list constructor.)



                Another footnote - don't put inner parens in expressions like list((...generator...)). Constructors can accept generator expressions directly.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited yesterday

























                answered yesterday









                Reinderien

                3,832821




                3,832821






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Code Review Stack Exchange!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    Use MathJax to format equations. MathJax reference.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f210869%2fadvent-of-code-2018-day-4-pythonic-sleepy-guards%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    How to make a Squid Proxy server?

                    Is this a new Fibonacci Identity?

                    19世紀