|
| 1 | +=begin |
| 2 | +Codewars. 24/04/20. 'Simple Fun #170: Sum Groups'. 6kyu. Given an array of |
| 3 | +integers, sum consecutive even numbers and consecutive odd numbers. Repeat |
| 4 | +the process while it can be done and return the length of the final array. |
| 5 | +Here is a refined version of the top solution on Codewars. |
| 6 | +1) We define our method sum_groups, which takes an array of integers as its |
| 7 | + argument. |
| 8 | +2) We return the size of the array unless the array still contains any |
| 9 | + consecutive even numbers. a.even? == b.even? essentially evaluates whether |
| 10 | + both a.even? and b.even? are equal to true or false so if both are true we |
| 11 | + don't return arr.size because we have consecutive evens. Likewise, if both |
| 12 | + are false we don't return the array size, because we have consecutive odds. |
| 13 | +3) If there are consecutives, we call sum_groups recursively with the chunking |
| 14 | + and summing process performed on the array. |
| 15 | +4) arr.chunk(&:even?) returns an array of arrays where each sub-array's first |
| 16 | + element is a true (if it's even) and a false (if it's odd); the second |
| 17 | + element in the sub-array is an array with all the consecutive values that |
| 18 | + match the conditions of the first element. |
| 19 | +5) We then call map(&:last) to remove those first elements (booleans) and now |
| 20 | + we have an array of arrays containing consecutive evens and consecutive odds. |
| 21 | +6) We map over the array of arrays and sum each sub-array, which essentially |
| 22 | + sums all the odd consecutives and all the even consecutives. |
| 23 | +7) Now we have a new array of integers. If there are no consecutives, the size |
| 24 | + of it is returned. If not, the chunk and sum process runs again. This |
| 25 | + process continues until we have no consecutive odds or evens, then we |
| 26 | + return the length of the final array. |
| 27 | +9) If our starting array is [2,1,2,2,6,5,0,2,0,5,5,7,7,4,3,3,9], after the |
| 28 | + first recursive call it will be [2,1,10,5,2,24,4,15]. We have consecutives |
| 29 | + [2,24,4] so a second recursive call is done, after which we have |
| 30 | + [2,1,10,5,30,15]. Now we have no consecutives, so we return the array size, |
| 31 | + 6. |
| 32 | +10) In this method, we could substitute a.even? == a.even? with |
| 33 | + a.odd? == b.odd?, and it would still work, because what we're comparing is |
| 34 | + equal boolean values, equal boolean values mean we have a consecutive, |
| 35 | + either even or odd. |
| 36 | +11) We could also substitute arr.chunk(&:even?) with arr.chunk(&:odd?), |
| 37 | + because regardless of which we use, consecutive odds and evens are grouped |
| 38 | + together as a result of either argument being placed in the chunk method. |
| 39 | +12) Also, all 3 uses of even? could simply be uses of odd? and our method |
| 40 | + still would work. |
| 41 | +=end |
| 42 | + |
| 43 | +def sum_groups(arr) |
| 44 | + return arr.size unless arr.each_cons(2).any? {|a,b| a.even? == b.even?} |
| 45 | + sum_groups(arr.chunk(&:even?).map(&:last).map(&:sum)) |
| 46 | +end |
| 47 | + |
| 48 | +=begin |
| 49 | +Here is a refined version of another solution submitted by a Codewars user, |
| 50 | +which uses the slice_when method instead of the chunk method. |
| 51 | +1) We call the slice_when method on the array. The slice_when method takes 2 |
| 52 | + block variables representing element before and element after and groups an |
| 53 | + array based on the block. It is similar to the chunk method. |
| 54 | +2) In our slice_when block we group together consecutive evens and odds in an |
| 55 | + array of arrays and store this in a variable groups. |
| 56 | +3) We return the array size if every sub-array in groups contains 1 element, |
| 57 | + which means we have no consecutives. |
| 58 | +4) If not, we resursively call the method with the sub-arrays - and hence |
| 59 | + consecutives - summed. |
| 60 | +5) Once there are no consecutive odds or evens, every sub-array in groups will |
| 61 | + contain 1 element, in which case the array size is returned. |
| 62 | +=end |
| 63 | + |
| 64 | +def sum_groups_sw(arr) |
| 65 | + groups = arr.slice_when {|a,b| a.even? != b.even?}.to_a |
| 66 | + return arr.size if groups.all? {|g| g.size == 1} |
| 67 | + sum_groups_sw(groups.map(&:sum)) |
| 68 | +end |
| 69 | + |
| 70 | +=begin |
| 71 | +Here is the method I found online that allowed me to pass the challenge, it's |
| 72 | +clever in that it doesn't sum the numbers at all, but it's not a method I |
| 73 | +would voluntarily use. |
| 74 | +1) We call the chunk method on our array of integers with odd? passed in as an |
| 75 | + argument and turn this from an enumerator into an array. We essentially |
| 76 | + create an array of arrays where the first element of each sub-array is a |
| 77 | + boolean (true if odd, false if even) and the second element of the sub-array |
| 78 | + is all the consecutive values matching the condition of the first element. |
| 79 | + We store this in a variable chunks. |
| 80 | +2) We return the size of the array passed in if it is equal to the size of it |
| 81 | + chunked, this basically means our original array has been chunked and |
| 82 | + mapped down to contain no consecutives. If not, we run the "mapped" process |
| 83 | + on chunks. |
| 84 | +3) In the "mapped" process - stored in the variable mapped - we call the map |
| 85 | + method on the chunks array of arrays. If the first value is true (meaning |
| 86 | + this was an odd number) and the size of the consecutive terms of this |
| 87 | + number is odd also, we convert that entire sub-array to 1. |
| 88 | +4) In all other cases, i.e. if the first element if false (meaning the |
| 89 | + number was even) or the first element is true but its consecutive terms are |
| 90 | + even in quantity, the sub-array is converted to 0. |
| 91 | +5) If our original array we're [2,1,2,2,6,5,0,2,0,5,5,7,7,4,3,3,9], after being |
| 92 | + chunked and mapped it would be [0,1,0,1,0,0,0,1]. Our method is then called |
| 93 | + resursively with mapped passed in, mapped becomes arr and the size of mapped |
| 94 | + is 8, so arr.size is 8. The size of mapped (arr) chunked is 6. So in this |
| 95 | + instance the array size is not returned and the mapped process runs again. |
| 96 | +6) Once mapped is chunked and mapped again it becomes [0,1,0,1,0,1], this new |
| 97 | + mapped is passed into the method again. This time mapped (arr) size (6) is |
| 98 | + equal to its chunks size (6). This gives us the size of the final array, 6. |
| 99 | +=end |
| 100 | + |
| 101 | +def sum_groups_ms(arr) |
| 102 | + chunks = arr.chunk(&:odd?).to_a |
| 103 | + return arr.size if arr.size == chunks.size |
| 104 | + mapped = chunks.map {|odd,terms| odd && terms.size.odd? ? 1 : 0} |
| 105 | + sum_groups(mapped) |
| 106 | +end |
0 commit comments