Compute max population given a list of people's birth and death year

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP





.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;







up vote
0
down vote

favorite












I recently came across a brief programming puzzle prompt (say that three times fast) which asked to return the year that population peaked.



The actual algorithm to compute it can be done several ways and is not particularly difficult, but I have recently been challenging myself to write .. ahem .. "more functional" code.



There are several tenets of FP, but a chief one is not producing what developers call "side effects". I'm hoping to get to a point where I do not rely as much on classic object-oriented patterns and feel more at ease diving right into the functional style. Of course this takes practice, so I decided to spend some extra time polishing up my code to meet my internal requirements:



val persons = Seq(
Person(2000, 2010),
Person(2000, 2005),
Person(1990, 2005)
)

// Create an array of population change.
val births = persons.groupBy(_.birthYear).mapValues(_.length)
val deaths = persons.groupBy(_.deathYear).mapValues(_.length)
val populationChanges = (births.keySet ++ deaths.keySet).map year =>
(year, births.getOrElse(year, 0) - deaths.getOrElse(year, 0))
.toMap

println("populationChanges", populationChanges)

val sortedYears = populationChanges.keys.toSeq.sorted
println(sortedYears)

// construct an array of total populations
val populations = populationChanges.toSeq
.sortBy(_._1)
.scanLeft(0) (a, b) => a + b._2
.tail
.zip(sortedYears) // this is a bit hacky
.map case(count, year) => (year, count)

println("pop", populations)
println("max pop", populations.maxBy(_._2)) // (2000, 3)


This is a demonstration of what I created after a few minutes of iteration.



I was readily able to produce a list of population changes and from there, performed further transformations to arrive at a list of total populations for each year. From there determining the year that population peaked was trivial.



However, I'm not fully satisfied with this solution. I could be being too dogmatic, but in my mind I would not be breaking scope of a map function to reference external values, even though it proved expedient in this case.







share|improve this question

























    up vote
    0
    down vote

    favorite












    I recently came across a brief programming puzzle prompt (say that three times fast) which asked to return the year that population peaked.



    The actual algorithm to compute it can be done several ways and is not particularly difficult, but I have recently been challenging myself to write .. ahem .. "more functional" code.



    There are several tenets of FP, but a chief one is not producing what developers call "side effects". I'm hoping to get to a point where I do not rely as much on classic object-oriented patterns and feel more at ease diving right into the functional style. Of course this takes practice, so I decided to spend some extra time polishing up my code to meet my internal requirements:



    val persons = Seq(
    Person(2000, 2010),
    Person(2000, 2005),
    Person(1990, 2005)
    )

    // Create an array of population change.
    val births = persons.groupBy(_.birthYear).mapValues(_.length)
    val deaths = persons.groupBy(_.deathYear).mapValues(_.length)
    val populationChanges = (births.keySet ++ deaths.keySet).map year =>
    (year, births.getOrElse(year, 0) - deaths.getOrElse(year, 0))
    .toMap

    println("populationChanges", populationChanges)

    val sortedYears = populationChanges.keys.toSeq.sorted
    println(sortedYears)

    // construct an array of total populations
    val populations = populationChanges.toSeq
    .sortBy(_._1)
    .scanLeft(0) (a, b) => a + b._2
    .tail
    .zip(sortedYears) // this is a bit hacky
    .map case(count, year) => (year, count)

    println("pop", populations)
    println("max pop", populations.maxBy(_._2)) // (2000, 3)


    This is a demonstration of what I created after a few minutes of iteration.



    I was readily able to produce a list of population changes and from there, performed further transformations to arrive at a list of total populations for each year. From there determining the year that population peaked was trivial.



    However, I'm not fully satisfied with this solution. I could be being too dogmatic, but in my mind I would not be breaking scope of a map function to reference external values, even though it proved expedient in this case.







    share|improve this question





















      up vote
      0
      down vote

      favorite









      up vote
      0
      down vote

      favorite











      I recently came across a brief programming puzzle prompt (say that three times fast) which asked to return the year that population peaked.



      The actual algorithm to compute it can be done several ways and is not particularly difficult, but I have recently been challenging myself to write .. ahem .. "more functional" code.



      There are several tenets of FP, but a chief one is not producing what developers call "side effects". I'm hoping to get to a point where I do not rely as much on classic object-oriented patterns and feel more at ease diving right into the functional style. Of course this takes practice, so I decided to spend some extra time polishing up my code to meet my internal requirements:



      val persons = Seq(
      Person(2000, 2010),
      Person(2000, 2005),
      Person(1990, 2005)
      )

      // Create an array of population change.
      val births = persons.groupBy(_.birthYear).mapValues(_.length)
      val deaths = persons.groupBy(_.deathYear).mapValues(_.length)
      val populationChanges = (births.keySet ++ deaths.keySet).map year =>
      (year, births.getOrElse(year, 0) - deaths.getOrElse(year, 0))
      .toMap

      println("populationChanges", populationChanges)

      val sortedYears = populationChanges.keys.toSeq.sorted
      println(sortedYears)

      // construct an array of total populations
      val populations = populationChanges.toSeq
      .sortBy(_._1)
      .scanLeft(0) (a, b) => a + b._2
      .tail
      .zip(sortedYears) // this is a bit hacky
      .map case(count, year) => (year, count)

      println("pop", populations)
      println("max pop", populations.maxBy(_._2)) // (2000, 3)


      This is a demonstration of what I created after a few minutes of iteration.



      I was readily able to produce a list of population changes and from there, performed further transformations to arrive at a list of total populations for each year. From there determining the year that population peaked was trivial.



      However, I'm not fully satisfied with this solution. I could be being too dogmatic, but in my mind I would not be breaking scope of a map function to reference external values, even though it proved expedient in this case.







      share|improve this question











      I recently came across a brief programming puzzle prompt (say that three times fast) which asked to return the year that population peaked.



      The actual algorithm to compute it can be done several ways and is not particularly difficult, but I have recently been challenging myself to write .. ahem .. "more functional" code.



      There are several tenets of FP, but a chief one is not producing what developers call "side effects". I'm hoping to get to a point where I do not rely as much on classic object-oriented patterns and feel more at ease diving right into the functional style. Of course this takes practice, so I decided to spend some extra time polishing up my code to meet my internal requirements:



      val persons = Seq(
      Person(2000, 2010),
      Person(2000, 2005),
      Person(1990, 2005)
      )

      // Create an array of population change.
      val births = persons.groupBy(_.birthYear).mapValues(_.length)
      val deaths = persons.groupBy(_.deathYear).mapValues(_.length)
      val populationChanges = (births.keySet ++ deaths.keySet).map year =>
      (year, births.getOrElse(year, 0) - deaths.getOrElse(year, 0))
      .toMap

      println("populationChanges", populationChanges)

      val sortedYears = populationChanges.keys.toSeq.sorted
      println(sortedYears)

      // construct an array of total populations
      val populations = populationChanges.toSeq
      .sortBy(_._1)
      .scanLeft(0) (a, b) => a + b._2
      .tail
      .zip(sortedYears) // this is a bit hacky
      .map case(count, year) => (year, count)

      println("pop", populations)
      println("max pop", populations.maxBy(_._2)) // (2000, 3)


      This is a demonstration of what I created after a few minutes of iteration.



      I was readily able to produce a list of population changes and from there, performed further transformations to arrive at a list of total populations for each year. From there determining the year that population peaked was trivial.



      However, I'm not fully satisfied with this solution. I could be being too dogmatic, but in my mind I would not be breaking scope of a map function to reference external values, even though it proved expedient in this case.









      share|improve this question










      share|improve this question




      share|improve this question









      asked Jan 25 at 8:49









      Leo Romanovsky

      1012




      1012




















          1 Answer
          1






          active

          oldest

          votes

















          up vote
          1
          down vote













          Your approach isn't bad if you want to do multiple analyses of the population over time. For programming puzzles I often find it useful to focus in on exactly what's being requested and leave the other interesting stuff for another day.



          persons.flatMap(p => Seq((p.birthYear, 1), (p.deathYear, -1)))
          .sorted
          .foldLeft((0,0,0))case ((mxYear,mxPop,curPop), (thisYear,dif)) =>
          if (curPop+dif > mxPop) (thisYear, curPop+dif, curPop+dif)
          else (mxYear, mxPop, curPop+dif)
          ._1 //res0: Int = 2000


          In this case the data is



          • traversed once - to attach a 1 or -1

          • sorted - to get all the years in order

          • traversed a 2nd and final time - to count the population changes and save the maxes





          share|improve this answer





















          • Great use of foldLeft!
            – Leo Romanovsky
            Jan 29 at 5:35










          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',
          convertImagesToLinks: false,
          noModals: false,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          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%2f185946%2fcompute-max-population-given-a-list-of-peoples-birth-and-death-year%23new-answer', 'question_page');

          );

          Post as a guest






























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          1
          down vote













          Your approach isn't bad if you want to do multiple analyses of the population over time. For programming puzzles I often find it useful to focus in on exactly what's being requested and leave the other interesting stuff for another day.



          persons.flatMap(p => Seq((p.birthYear, 1), (p.deathYear, -1)))
          .sorted
          .foldLeft((0,0,0))case ((mxYear,mxPop,curPop), (thisYear,dif)) =>
          if (curPop+dif > mxPop) (thisYear, curPop+dif, curPop+dif)
          else (mxYear, mxPop, curPop+dif)
          ._1 //res0: Int = 2000


          In this case the data is



          • traversed once - to attach a 1 or -1

          • sorted - to get all the years in order

          • traversed a 2nd and final time - to count the population changes and save the maxes





          share|improve this answer





















          • Great use of foldLeft!
            – Leo Romanovsky
            Jan 29 at 5:35














          up vote
          1
          down vote













          Your approach isn't bad if you want to do multiple analyses of the population over time. For programming puzzles I often find it useful to focus in on exactly what's being requested and leave the other interesting stuff for another day.



          persons.flatMap(p => Seq((p.birthYear, 1), (p.deathYear, -1)))
          .sorted
          .foldLeft((0,0,0))case ((mxYear,mxPop,curPop), (thisYear,dif)) =>
          if (curPop+dif > mxPop) (thisYear, curPop+dif, curPop+dif)
          else (mxYear, mxPop, curPop+dif)
          ._1 //res0: Int = 2000


          In this case the data is



          • traversed once - to attach a 1 or -1

          • sorted - to get all the years in order

          • traversed a 2nd and final time - to count the population changes and save the maxes





          share|improve this answer





















          • Great use of foldLeft!
            – Leo Romanovsky
            Jan 29 at 5:35












          up vote
          1
          down vote










          up vote
          1
          down vote









          Your approach isn't bad if you want to do multiple analyses of the population over time. For programming puzzles I often find it useful to focus in on exactly what's being requested and leave the other interesting stuff for another day.



          persons.flatMap(p => Seq((p.birthYear, 1), (p.deathYear, -1)))
          .sorted
          .foldLeft((0,0,0))case ((mxYear,mxPop,curPop), (thisYear,dif)) =>
          if (curPop+dif > mxPop) (thisYear, curPop+dif, curPop+dif)
          else (mxYear, mxPop, curPop+dif)
          ._1 //res0: Int = 2000


          In this case the data is



          • traversed once - to attach a 1 or -1

          • sorted - to get all the years in order

          • traversed a 2nd and final time - to count the population changes and save the maxes





          share|improve this answer













          Your approach isn't bad if you want to do multiple analyses of the population over time. For programming puzzles I often find it useful to focus in on exactly what's being requested and leave the other interesting stuff for another day.



          persons.flatMap(p => Seq((p.birthYear, 1), (p.deathYear, -1)))
          .sorted
          .foldLeft((0,0,0))case ((mxYear,mxPop,curPop), (thisYear,dif)) =>
          if (curPop+dif > mxPop) (thisYear, curPop+dif, curPop+dif)
          else (mxYear, mxPop, curPop+dif)
          ._1 //res0: Int = 2000


          In this case the data is



          • traversed once - to attach a 1 or -1

          • sorted - to get all the years in order

          • traversed a 2nd and final time - to count the population changes and save the maxes






          share|improve this answer













          share|improve this answer



          share|improve this answer











          answered Jan 26 at 9:27









          jwvh

          45625




          45625











          • Great use of foldLeft!
            – Leo Romanovsky
            Jan 29 at 5:35
















          • Great use of foldLeft!
            – Leo Romanovsky
            Jan 29 at 5:35















          Great use of foldLeft!
          – Leo Romanovsky
          Jan 29 at 5:35




          Great use of foldLeft!
          – Leo Romanovsky
          Jan 29 at 5:35












           

          draft saved


          draft discarded


























           


          draft saved


          draft discarded














          StackExchange.ready(
          function ()
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f185946%2fcompute-max-population-given-a-list-of-peoples-birth-and-death-year%23new-answer', 'question_page');

          );

          Post as a guest













































































          Popular posts from this blog

          Chat program with C++ and SFML

          Function to Return a JSON Like Objects Using VBA Collections and Arrays

          Will my employers contract hold up in court?