# Count edits accounting for grace period

When you edit a post on SE, any further edits within a 5-minute grace period are merged into it. Given a list of times you edit a post, count the edits not in a grace period.

Say you edit at minutes [0,3,4,7,9,10,11,12]. This results in 3 edits at times [0,7,12], with the rest happening in their grace periods.

0:  [3,4]
7:  [9,10,11]
12: []

• The first edit is at minute 0. The edits at minutes 3 and 4 are within its 5-minute grace period, and so don't count.
• The second edit is at minute 7. The edits at minutes 9, 10, 11 are within its grace period.
• The third edit at minute 12 is just past the edge of the 5-minute grace period starting at minute 7.

So, the output is 3.

The list of times in minutes will be an list of increasing integers. The first number will always be 0 for the initial posting, which we count as an edit.

Test cases:

[0]
[0,3,5,7]
[0,3,4,7,9,10,11,12]
[0,30,120]
[0,4,8,12,16]
[0,4,8,12,16,20]
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19]
[0,5,10,15,20]
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]
[0,1,4,5,9,11,12,14,16,18,23,24,26,28,29,30]


Outputs:

1
2
3
3
3
3
4
5
5
6


For ease of copying, here are the inputs, outputs, and input/output pairs:

[[0], [0, 3, 5, 7], [0, 3, 4, 7, 9, 10, 11, 12], [0, 30, 120], [0, 4, 8, 12, 16], [0, 4, 8, 12, 16, 20], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], [0, 5, 10, 15, 20], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], [0, 1, 4, 5, 9, 11, 12, 14, 16, 18, 23, 24, 26, 28, 29, 30]]
[1, 2, 3, 3, 3, 3, 4, 5, 5, 6]
[([0], 1), ([0, 3, 5, 7], 2), ([0, 3, 4, 7, 9, 10, 11, 12], 3), ([0, 30, 120], 3), ([0, 4, 8, 12, 16], 3), ([0, 4, 8, 12, 16, 20], 3), ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19], 4), ([0, 5, 10, 15, 20], 5), ([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20], 5), ([0, 1, 4, 5, 9, 11, 12, 14, 16, 18, 23, 24, 26, 28, 29, 30], 6)]


var QUESTION_ID=141949,OVERRIDE_USER=20260;function answersUrl(e){return"https://api.stackexchange.com/2.2/questions/141949/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"https://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

• Although it's really annoying if your edit doesn't make the grace period, because then you have to use your new grace period to make it look as if you meant to edit it that way all along...
– Neil
Sep 14 '17 at 0:33

# JavaScript, 36 bytes

f=$=>$>f&&1+f($.filter(b=>b-$[0]>4))


Try it online!

### How it works

In each recursive call we delete all elements from the array which are more than 4 minutes distant from the first element.
There is a little trick with variable name $. The check $>f firstly converts the array to a string and then compare it to the string representation of the function f and then compares them lexicographically. The first character of stringified array is a digit and therefore only one-char variable name whose ascii index is smaller than indices of all digits is $. Replacing $ with any other variable name will always return false.

• I love this site because of answers like these. Sep 7 '17 at 6:44
• Very nice trick! Sep 7 '17 at 9:09
• Oh, now, that is a great trick! Sep 7 '17 at 9:51

# Mathematica, 464037 33 bytes

(i=1;j=0;#-j<5||(i++;j=#)&/@#;i)&


### Explanation

i=1;j=0


Set i to 1 and j to 0.

... /@#


Map onto all elements of the input...

#-j<5||(i++;j=#)&


If (element) - j < 5 is false, then increment i and set j to the element (short-circuit evaluation).

;i


Output i.

# Husk, 8 bytes

Γ(→₀f>+4


Try it online!

## Explanation

Γ(→₀f>+4  Implicit input, a list of numbers.
Γ(        Deconstruct into head n and tail x (if empty, return 0).
f>+4  Keep those elements of x that are greater than n+4.
₀      Call main function recursively on the result.
→       Increment.


# Python 2, 58 bytes

a=input()
x=[0]
for k in a:x+=[k]*(k-x[-1]>4)
print len(x)


Try it online!

• Saved 2 bytes thanks to @Mr. Xcoder.

## 49 bytes

f=lambda a:a>[]and-~f([x for x in a if x-a[0]>4])


Using the recursive method shown in @ThePirateBay's solution.

• Saved a byte thanks to @Mr. Xcoder.
• Saved 2 bytes thanks to @Halvard Hummel.

Try it online!

• and 1+f(...) can be replaced by and-~f(...) for 49 bytes Sep 7 '17 at 7:27
• @Mr.Xcoder Oh, can't forget all those bitwise tricks. Sep 7 '17 at 7:37
• x=a[:1] is equivalent to x=[0], since the question explicitly states that the first element is always 0 (62 bytes) Sep 7 '17 at 8:01
• 47 bytes recursive Sep 7 '17 at 8:42

# J, 20 bytes

[:#(,}.~5>(-{.))/@|.


Try it online!

## Explanation

[:#(,}.~5>(-{.))/@|.  Input: array A
|.  Reverse
/@    Reduce from right-to-left
-            Subtract with LHS
5>              Less than 5
}.~                Drop that many from
,                   Join
[:#                   Length


# MATLAB, 34 bytes

@(x)nnz(uniquetol(x+1,4/max(x+1)))


Anonymous function that inputs an array and outputs a number.

This uses the uniquetol function, specifically its form y = uniquetol(x, t), which gives y containing unique elements of x with tolerance t. In doing so, the function seems to follow a "lazy" approach: sort x, pick its first entry, and keep skipping entries for as long as they are within tolerance of the latest picked entry. That is exactly what is needed here.

The uniquetol function automatically scales the specified tolerance by the maximum absolute value in a. This is why we need the division here. x+1 is used instead of x to avoid division by 0.

Verification of test cases:

>> f = @(x)nnz(uniquetol(x+1,4/max(x+1)));
>> inputs = {...
[0] ...
[0,3,5,7] ...
[0,3,4,7,9,10,11,12] ...
[0,30,120] ...
[0,4,8,12,16] ...
[0,4,8,12,16,20] ...
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19] ...
[0,5,10,15,20] ...
[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] ...
[0,1,4,5,9,11,12,14,16,18,23,24,26,28,29,30] ...
};
>> outputs = cellfun(f, inputs)
outputs =
1     2     3     3     3     3     4     5     5     6

• TIL about uniquetol... Introduced in R2015a. I have R2014b :( Nice answer :) Sep 7 '17 at 12:59
• @Stewie I knew it existed, but I think this is the first time I use it Sep 7 '17 at 14:53

# 05AB1E, 2019181514 11 bytes

v®y‹iy4+©\¼


Explanation:

v          # loop on input
®          # push register_c, start at -1
y‹i         # if current item greater than last item
y4+         # push new max on stack
©\          # push new max on register_c, and pop it from stack
¼           # increment counter_variable
# implicit print of counter_variable


Try it online!

Edit

• -3 bytes thanks to Riley and the usage of counter_variable
• no need for counter_variable after all
• -3 bytes again thanks to Riley and the usage of register_c
• You can use the counter variable to save 3 bytes: ¼4¹vDy‹i¼y4+}}¾ Sep 7 '17 at 15:03
• oooooh, there is a counter variable, that's handy! Thank you!! Sep 7 '17 at 15:29
• 11 bytes: v®y‹iy4+©\¼ Sep 7 '17 at 16:20

## Husk, 6 bytes

Lüo<+5


Try it online!

  o<+5        a function that takes two arguments and checks if
the second is less than the the first plus 5
ü            remove equal elements from the input list using the above
function as the equality test
L             return the length of the remaining list

• Whoa, I didn't realize ü works like that! That's very handy. Sep 7 '17 at 19:45
• @Zgarb: I first tried ġ but it doesn't work, whereas Haskell's groupBy works: length.groupBy((>).(+5)). Then I found ü which also leads to a shorter Haskell equivalent: nubBy.
– nimi
Sep 7 '17 at 19:50

f(x:y)=f[z|z<-y,z-4>x]+1
f x=0


Try it online!

Saved 1 byte thanks to Zgarb

• z-4>x should save a byte. Sep 7 '17 at 7:29
• @Zgarb What was I thinking??? Thanks! :) Sep 7 '17 at 8:29

# Perl 5, 54 bytes

52 bytes of code +2 for -ap

{$_++;$p=shift@F;1while$F[0]<$p+5&&shift@F;@F&&redo}


Try it online!

# 05AB1E, 14 bytes

æʒ¬s¥4›Ps_*}θg


Try it online!

• That's insane, æ for superset, that's a great trick! Sep 7 '17 at 14:22
• @CyrilGandon æ means "powerset". Sep 7 '17 at 14:24

&nr5+sp[[lL[pp2$]p5+sp><]]>  Try it online! It took a long time to get here. # Retina, 32 26 bytes .+$*11
(1+)(¶1{1,4}\1)*\b


Try it online! Explanation:

.+
$*11  Convert to unary, but add 1, because 0 is a tricky concept in Retina. (1+)(¶1{1,4}\1)*\b  Count the number of edits, but include all the grace edits in each match. # MATL, 13 12 bytes ttX<4+>)t}@  ### Explanation  % Do..while t % Duplicate. Takes input (implicitly) the first time tX< % Duplicate and get minimum, i.e the first entry 4+ % Add 4 > % Greater than? Element-wise ) % Keep entries that fulfill that t % Duplicate. This is used as loop condition } % Finally (execute at the end of the loop) @ % Push number of iterations. This is the output % End (implicit). A new iteration is run if top of the stack is truthy  # Pyth, 14 bytes L&lbhyfg-Thb5b  This is a recursive function. Call it with y[0 1 2 3 4 5 6 7 8), where [...) is your list. Alternatively, Try it here! or Verify all the test cases. # Explanation This is roughly equivalent to the Python solution. A translation would give the following results: def y(b): return (len(b) and y(filter(lambda T:T>=b[0]+5,b)) + 1)  ### Code Breakdown L&lbhyfg-Thb5b - Function called y that accepts a list parameter b. L - Define the function. lb - The length of b... & - ... Logical AND ... h - Increment by 1. y - The result given by calling the function recursively on the following: f b - b filtered... -Thb - ... For the elements whose difference compared to the first element... g 5 - ... Is greater than or equal to 5.  • I am trying to find a workaround with .U. Suggestions are welcome Sep 7 '17 at 8:14 # Java 8, 78616059 56 bytes Port of @JungHwanMin's answer a->{int i=0;for(int l:a)if(l-a[i]>4)a[++i]=l;return-~i;}  Try it online! # C# .NET, 63 bytes a=>{int e=0;foreach(int l in a)if(l-a[e]>4)a[++e]=l;return-~e;}  Explanation: Try it here. a=>{ // Method with integer-array parameter and integer return-type int e=0; // Amount of edits (starting at 0) foreach(int l in a) // Loop over the input-array if(l-a[e]>4) // If the current value minus the current edit is larger than 4: a[++e]=l; // Raise the edit-count by 1 first, // and set the current value to this next current edit // End of loop (implicit / single-line body) return-~e; // Return the amount of edits + 1 } // End of method  # Pyth, 25 bytes J1K5FN.+Q=-KNIg0K=hJ=K5;J  Try it here: http://pyth.herokuapp.com • Keeping it as a full program, you can get 21 bytes. Sep 7 '17 at 8:00 # Proton, 40 bytes Heavily inspired by the Python solution. f=a=>len(a)and-~f(filter(x=>x>a[0]+4,a))  Try it online! # Kotlin, 52 bytes Posting as a function, if this is not acceptable I will change it to a method ## Submission {var x=it[0] var o=1 it.map{if(it>x+4){o++ x=it}} o}  ## Beautified { // Last counted edit var x=it[0] // Current edit total var o = 1 // For each edit it.map{ // If it was 5 or more minutes ago if (it>x+4) { // Increase edit count o++ // Make it the last counted edit x=it } } // Return the edit count o }  ## Test var r:(IntArray)->Int= {var x=it[0] var o=1 it.map{if(it>x+4){o++ x=it}} o} fun main(args: Array<String>) { println(r(intArrayOf(0))) println(r(intArrayOf(0,3,5,7))) println(r(intArrayOf(0,3,4,7,9,10,11,12))) println(r(intArrayOf(0,30,120))) println(r(intArrayOf(0,4,8,12,16))) println(r(intArrayOf(0,4,8,12,16,20))) println(r(intArrayOf(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19))) println(r(intArrayOf(0,5,10,15,20))) println(r(intArrayOf(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20))) println(r(intArrayOf(0,1,4,5,9,11,12,14,16,18,23,24,26,28,29,30))) }  TryItOnline # PowerShell, 74 bytes for($x,$y=$args[0];$y;$x,$y=$y){if($l-le$x-5){$i++;$l=$x}}$i+1+($l-le$x-5)


Iterative solution. Lengthy because of fenceposting on the for loop requiring an additional check on the end. Golfing suggestions welcome.

We take input $args[0] as a literal array, peel off the first element into $x and the remaining into $y. Then, so long as there are elements still in $y, we loop.

Each iteration, we check whether the current timestamp $x is 5 or more away from the $last edit timestamp. If so, we increment our counter $i++ and set our timestamp to be current. Then, on the iteration of the loop, we peel off the next element into $x and leave the remaining in $y. Once we're out of the loop, we output $i, plus 1 for the initial edit, plus whether the final timestamp is more than five away from the last edit (with the Boolean value implicitly cast to integer). That result is left on the pipeline and output is implicit.

Try it online!

# R, 52 bytes

function(l){while(sum(l|1)){l=l[l-l[1]>=5]
F=F+1}
F}


Try it online!

Simple anonymous function that iteratively removes elements from the list that are less than 5 away from the first element until the list is empty, then returns the counter.

## Clojure, 53 bytes

#(count(set(reductions(fn[r v](if(<(- v r)5)r v))%)))


This keeps track of the "edit starting times", and then returns their distinct count.

# Japt, 14 bytes

Ê©1+ßUf_aUg)>4


Try it

## Explanation

Implicit input of array U

Ê


Get the length of U.

©


Logical AND (&&) - only execute the following if Ê is truthy (non-zero).

ß


Recursive call.

Uf_


Filter (f) U by passing each element through a function.

aUg


Get the difference (a) between the current element and the first element (g) of U.

>4


Greater than 4?

1+


Add 1.

Implicit output of resulting integer.

# Jelly, 11 bytes

+4Ḣ<x@µÐĿL’


Try it online!

## Explanation

+4Ḣ<x@µÐĿL’  Input: array A
µÐĿ    Repeat until the results converge
<           Greater than
x@         Copy only the true values
L   Length
’  Decrement


## 12 bytes

;I4<1;x@;ð/L


Try it online!

## Explanation

;I4<1;x@;ð/L  Input: array A
ð/   Reduce A from left-to-right using
;               Concatenate
I              Increment
4<            Greater than 4
1;          Prepend 1
x@        Times each of
;       Concatenate
L  Length