Maximum array sum - arrays

I am given an array of integers. I need to find max sum of its elements so that any two elements are not neighbors. Example : sum(2, 5, 2) = 5 because we choose just 5; sum(3, 10, 2, 4, 10) = 20 because we choose 10 and 10; sum(10, 12, 5, 2) = 15 because we choose 10 and 5.
How can it be done using any programming language?
I have been working on this problem for several hours and the only thing I understand that it should use DP.

So I've implemented a solution in Java without using DP; I've simply used recursion. I'll try to explain it a bit.
First of all, we have to find a base case:
if the array length is == 1, then sum(array) = array[0]
besides, if the array length is == 2, then sum(array) = max(array[0],array[1])
Now, let's get to the general case of array.length = n. We have to decide whether or not array[n-1] is part of the solution; that is, if sum, launched on the first n-1 elements, is smaller than sum launched on the first n-2 elements + nth element of the array.
In a nutshell, we are saying "Is it better to consider the nth element or its neighbour (nth-1)?" Indeed, we cannot consider both, therefore we have to choose one.
private static int sum (int[] array) {
return aux (array,array.length);
}
private static int aux (int[] array, int upTo) {
if (upTo == 1)
return array[0];
else if (upTo == 2)
return (array[1] > array[0])
? array[1] : array[0];
else {
int tmpMax1 = aux (array,upTo-1);
int tmpMax2 = aux (array,upTo-2) + array[upTo-1];
return (tmpMax2 > tmpMax1)
? tmpMax2 : tmpMax1;
}
}
public static void main(String[] args) {
//just a bunch of simple tests. Too lazy to use JUnit
System.out.println(sum(new int[]{2}) + " = 2");
System.out.println(sum(new int[]{2, 5}) + " = 5");
System.out.println(sum(new int[]{2, 5, 2}) + " = 5");
System.out.println(sum(new int[]{3, 10, 2, 4, 10}) + " = 20");
System.out.println(sum(new int[]{10, 12, 5, 2}) + " = 15");
System.out.println(sum(new int[]{10, 12, 5, 2,100}) + " = 115");
System.out.println(sum(new int[]{10, 10, 10, 10,100}) + " = 120");
}

Related

Maximum sum such that no two elements are adjacent

Now the available solution every where is to have an include and exclude sum . At the end max of these two will give me the output.
Now initially I was having difficulty to understand this algorithm and I thought why not going in a simple way.
Algo:
Loop over the array by increasing array pointer two at a time
Calculate the odd positioned element sum in the array
Calculate the even positioned element sum
At the end, take max of this two sum.
in that way, I think complexity will be reduced to half O(n/2)
Is this algo correct?
It's a case of dynamic programming. The algorithm is:
Do not take (sum up) any non-positive items
For positive numbers, split the problem in two: try taking and skiping the item and return the maximum of these choices:
Let's show the 2nd step, imagine we are given:
[1, 2, 3, 4, 5, 6, 10, 125, -8, 9]
1 is positive, that's why
take_sum = max(1 + max_sum([3, 4, 5, 6, 10, 125, -8, 9])) // we take "1"
skip_sum = max_sum([2, 3, 4, 5, 6, 10, 125, -8, 9]) // we skip "1"
max_sum = max(take_sum, skip_sum)
C# implementation (the simplest code in order to show the naked idea, no further optimization):
private static int BestSum(int[] array, int index) {
if (index >= array.Length)
return 0;
if (array[index] <= 0)
return BestSum(array, index + 1);
int take = array[index] + BestSum(array, index + 2);
int skip = BestSum(array, index + 1);
return Math.Max(take, skip);
}
private static int BestSum(int[] array) {
return BestSum(array, 0);
}
Test:
Console.WriteLine(BestSum(new int[] { 1, -2, -3, 100 }));
Console.WriteLine(BestSum(new int[] { 100, 8, 10, 20, 7 }))
Outcome:
101
120
Please, check, that your initial algorithm returns 98 and 117 which are suboptimal sums.
Edit: In real life you may want to add some optimization, e.g. memoization and special cases tests:
private static Dictionary<int, int> s_Memo = new Dictionary<int, int>();
private static int BestSum(int[] array, int index) {
if (index >= array.Length)
return 0;
int result;
if (s_Memo.TryGetValue(index, out result)) // <- Memoization
return result;
if (array[index] <= 0)
return BestSum(array, index + 1);
// Always take, when the last item to choose or when followed by non-positive item
if (index >= array.Length - 1 || array[index + 1] <= 0) {
result = array[index] + BestSum(array, index + 2);
}
else {
int take = array[index] + BestSum(array, index + 2);
int skip = BestSum(array, index + 1);
result = Math.Max(take, skip);
}
s_Memo.Add(index, result); // <- Memoization
return result;
}
private static int BestSum(int[] array) {
s_Memo.Clear();
return BestSum(array, 0);
}
Test:
using System.Linq;
...
Random gen = new Random(0); // 0 - random, by repeatable (to reproduce the same result)
int[] test = Enumerable
.Range(1, 10000)
.Select(i => gen.Next(100))
.ToArray();
int evenSum = test.Where((v, i) => i % 2 == 0).Sum();
int oddSum = test.Where((v, i) => i % 2 != 0).Sum();
int suboptimalSum = Math.Max(evenSum, oddSum); // <- Your initial algorithm
int result = BestSum(test);
Console.WriteLine(
$"odd: {oddSum} even: {evenSum} suboptimal: {suboptimalSum} actual: {result}");
Outcome:
odd: 246117 even: 247137 suboptimal: 247137 actual: 290856
dynamic programming inclusion exclusion approach is correct your algorithm would not work for test cases like 3 2 7 10 in this test case the two elements we take are 3 10 and sum is 13 instead of 3,7 or 2,10.may you understand what i am saying and for further clarity code is below
Java Implementation
public int maxSum(int arr[]) { // array must contain +ve elements only
int excl = 0;
int incl = arr[0];
for (int i = 1; i < arr.length; i++) {
int temp = incl;
incl = Math.max(excl + arr[i], incl);
excl = temp;
}
return incl;
}

Finding Second Smallest Element in Array

I'm trying to find the second smallest element in an array of n elements using only n + ceil(lg n) - 2 comparisons. The hint in CLRS says to find the smallest element.
This takes n - 1 comparisons so I'm left with ceil(lg n) - 1 comparisons to find the second smallest, once I know the largest.
Any ideas?
Thanks,
bclayman
Let's say we've got a list a1...an with n being a power of 2.
First pair the elements up, let's say a1 with a2, a3 with a4 and so on, and compare them with each other. This gives you n/2 comparisons.
Advance all the winners to the next round, which only has n/2 elements now, and repeat the same process. This requires n/4 more comparisons.
Repeat the above until you've only got 1 element left, the ultimate winner. To get there you had to do n/2 + n/4 + ... + 1 = n-1 comparisons.
That's great but which one could be the second smallest? Well, it has to be one of the elements your winner had beaten along the way to the top. There are lg n such losers, so you need to compare them amongst each other to find the smallest (requiring a further lg n - 1 comparisons).
And the smallest of the losers is the second smallest overall.
It's easy to prove why the above method always finds the second smallest: since it's smaller than every element but the ultimate winner, it would win every round apart from the one against the champion.
If n isn't a power of 2, the process is almost exactly the same, except the list of losers will be as long as it would be for the next exact power of 2 which is why you end up with ceil(lg n).
Here is a basic implementation in JavaScript, not sure it fully respects your O() requirements in all cases though. The initial array will also affect the comparison count.
var elements = [ 12, 1 , 3, 4, 65, 7, -43, 8, 3, 8, 45, 2 ];
var nCompare = 0;
var smallest = elements[0], smaller = elements[0];
for(var i = 1; i < elements.length; ++i) {
++nCompare;
if(elements[i] < smaller) {
++nCompare;
if(elements[i] < smallest) {
smaller = smallest;
smallest = elements[i];
}
else
smaller = elements[i];
}
}
document.body.innerHTML = '<pre>\n' +
'Elements: [ ' + elements.join(', ') + ' ]\n' +
'# element: ' + elements.length + '\n' +
'\n' +
'Smallest: ' + smallest + '\n' +
'2nd smallest: ' + smaller + '\n' +
'# compare: ' + nCompare +
'</pre>';
Below is a solution with O(n) complexity in Java:
public class MainClass {
public static void main(String[] args) {
int[] a = { 4, 2, 8, -2, 56, 0 };
c(a);
}
private static void c(int[] a) {
int s = Integer.MAX_VALUE;
int ss = Integer.MAX_VALUE;
for (int i : a) {
if (i < s) {
ss = s;
s = i;
} else if (i < ss) {
ss = i;
}
}
System.out.println("smallest : " + s + " second smallest : " + ss);
}
}
Output : smallest : -2 second smallest : 0
I think their is no need to for cel(log n) -1 additional comparison as it can be done in O(n) only i.e in one scan with the help of an extra variable as given below:-
for(i,0,no_of_elem-1)
{
if(min>elem[i])
{
second_smallest=min;
min=elem[i];
}
}
You just store previous minimum in a variable as that will be your answer after scanning all elements.

Pseudocode - Arrays - Maxrun

I'm trying to solve a problem but I have difficulties with algorithms.
I have to write pseudocode for an iterative algorithm maxRun(A) that takes an array A of integer as input and return the maximal length of a run in A.
The subarray A[k...l] is a run if A[j] <= A[j + 1] for all j where k <= j < l. So it is a non decreasing segment of A.
Ex. A = [1,5,2,3,4,1], the max length would be 3 [2,3,4].
Thanks.
Simple Java implementation:
public class FindRun {
public static int maxRun(int[] a) {
int max = 0;
int index = 0;
int previous = a[0] + 1;
int run = 0;
while (index < a.length) {
if (a[index] >= previous) {
run++;
} else {
max = Math.max(max, run);
run = 1;
}
previous = a[index];
index++;
}
return Math.max(max, run);
}
public static void main(String[] args) {
System.out.println(maxRun(new int[] { 1, 5, 2, 3, 4, 1 }));
}
}
Here's a solution similar to #Michael's, in Ruby:
a = [1,5,2,3,4,1]
r = [1]
(1...a.size).each { |i| r << ((a[i] == a[i-1] + 1) ? r[i-1] + 1 : 1) }
r.max #=> 3
r.index(r.max) #=> 4
indicating the the maximum run is of length 3 and ends at a offset 4; that is, the run 2,3,4. I will now explain the algorithm I used. For those who don't know Ruby, this will also give you a taste of the language:
r = [1] creates an array with one element, whose value is 1. This is read, "The longest run ending at a offset 0 is of length 1.
(1...a.size) is the sequence 1, 2, 3, 4, 5. Three dots between 1 and a.size means the sequence ends with a.size - 1, which is 5.
each causes the following block, enclosed by {} to be executed once for each element of the sequence. The block variable i (in |i|) represents the sequence element.
r << x means add x to the end of the array r.
the expression to the right of r << says, "if the element of a at index i is one greater than element of a at index i-1, then the length of the run ending at index i is one greater than the length of the run ending at index i-1; else, a new run begins, whose length at offset i is 1.
After each is finished:
# r => [1, 1, 1, 2, 3, 1]
All that is required now is to find the element of r whose value is greatest:
r.max #=> 3
and the associated index:
r.index(r.max) #=> 4
Actually, the code above would more typically be written like this:
(1...a.size).each_with_object([1]) {|i,r| r << a[i] == a[i-1]+1 ? r[i-1]+1 : 1}
start, length = r.index(r.max) + 1 - r.max, r.max #=> 2, 3
Alternatively, we could have made r a hash (call it h) rather than an array, and written:
(1...a.size).each_with_object({0 => 1}) {|i,h|
h[i] = a[i] == a[i-1]+1 ? h[i-1]+1 : 1}.max_by {|_,v| v} #=> [4, 3]

array index in heapsort

In reading in Chapter 14 of Jon Bentley's "Programming Pearls", 2nd Edition, I understand that heaps use a one-based array and the easiest approach in C is to declare x[n+1] and waste element x[0] (page 148).
On page 157, Jon listed the complete heapsort pseudo code:
for i = [2, n]
siftup(i)
for (i = n; i >= 2; i--)
swap(1, i)
siftdown(i - 1)
Here is an implementation in C. However, the array index starts with 0, instead of 1.
void heapSort(int numbers[], int array_size)
{
int i, temp;
// Qiang: shouldn't the stop-condition be i >= 1?
for (i = (array_size / 2)-1; i >= 0; i--)
siftDown(numbers, i, array_size);
for (i = array_size-1; i >= 1; i--)
{
// Qiang: shouldn't the swap be done with numbmers[1], instead of numbers[0]?
temp = numbers[0];
numbers[0] = numbers[i];
numbers[i] = temp;
siftDown(numbers, 0, i-1);
}
}
void siftDown(int numbers[], int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 <= bottom) && (!done))
{
if (root*2 == bottom)
maxChild = root * 2;
else if (numbers[root * 2] > numbers[root * 2 + 1])
maxChild = root * 2;
else
maxChild = root * 2 + 1;
if (numbers[root] < numbers[maxChild])
{
temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;
}
else
done = 1;
}
}
My worry is, if the array starts with index 0, then the following properties will not hold (as written on page 148 in Jon's book):
leftchild(i) = 2*i
rightchild(i) = 2*i+1
parent(i) = i/2
It looks to me that the properties here only hold when the i starts with 1.
What strikes me is that the analysis part in the implementation used an array starting with index 1, while the implementation part used an array starting with index 0 and didn't waste the first element.
Am I missing anything here?
Edited
With help from interjay, I realized the error contained in the original implementation, which could be shown with a testing input array of {66,4,23,4,78,6,44,11,22,1,99}.
Changed the original siftDown() function a little bit to adjust the relationship between the index of parent and those of its children:
void siftDown(int numbers[], int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 + 1 <= bottom) && (!done))
{
if (root*2 + 1 == bottom ||
numbers[root * 2 + 1] > numbers[root * 2 + 2])
maxChild = root * 2 + 1;
else
maxChild = root * 2 + 2;
if (numbers[root] < numbers[maxChild])
{
temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;
}
else
done = 1;
}
}
Credits go to interjay, :-)
Afterword:
It looks the same error doesn't appear in the implementations in wikibooks and algorithmist. Hooray!
The heap elements can be stored starting with index 0 or index 1, the decision on which to use is up to you.
If the root element is at index 1, the mathematical relations between parent and child indices are simple as you've shown above, and for that reason many books choose to teach it that way.
If the root is at index 0, you'd get these relations instead:
leftchild(i) = 2*i+1
rightchild(i) = 2*i+2
parent(i) = (i-1) / 2
It doesn't matter which one you pick as long as you are consistent.
The C code you've shown seems incorrect to me. It starts with array index 0, but uses the parent/child relations appropriate for starting with index 1.
A reusable implementation of heapsort would want to start at a root index of 0 so the user could use a normal (0 based) array with it. You wouldn't want to require the user to allocate an extra member and start the array at index 1 just so they can use your heapsort function. You do need to use the modified parent/child calculations that #interjay shows.
Replying to little old thread, thought my small contribution might helps future visitors.
Experts please validate and correct my logic if I missed any scenarios.
Considered Qiang Xu link and interjay zero based index logic.
And here is the C# code and tested with the below inputs.
//-----------------------------------------------------------------------------------------------------------------------------------------------
// Input Arrays :
int[] ErrCaseArry = new int[] { 66, 4, 23, 4, 78, 6, 44, 11, 22, 1, 99};
int[] GenCaseArry = new int[] { 30, 20, 40, 10, 90, 160, 140, 100, 80, 70 };
int[] NearlySortedArry = new int[] { 1, 2, 3, 4, 6, 5 };
int[] FewSortedArry1 = new int[] { 3, 2, 1, 4, 5, 6 };
int[] FewSortedArry2 = new int[] { 6, 2, 3, 1, 5, 4 };
int[] ReversedArry1 = new int[] { 6, 5, 4, 3, 2, 1 };
int[] FewDuplsArry2 = new int[] { 1, 3, 1, 2, 1, 3 };
int[] MoreDuplsArry3 = new int[] { 1, 1, 2, 2, 1, 2 };
//-----------------------------------------------------------------------------------------------------------------------------------------------
public void HeapSort(int[] listToSort)
{
int LastChildIndex = listToSort.Length -1;
int parentElementIndex = ((LastChildIndex - 1)/ 2);
//1. Use this loop to Construct Heap Array (Max/Min) by using Heapify function on every node.
while (parentElementIndex >= 0) // (N - 1) / 2 to 0
{
Heapify(listToSort, parentElementIndex, LastChildIndex); // (N - 1) / 2 & Lenght - 1
parentElementIndex--;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
AppendArrayToResultString("Max Heap\t", listToSort);
//2. Heap sort algorithm takes largest element off the heap and places it at the end of an array.
// This phase continue until all the elements are placed in the array that are in sorted order.
int sortedElementIndex = listToSort.Length - 1;
//-----------------------------------------------------------------------------------------------------------------------------------------------
// In this loop get Largest Element to Zero'th postion and move to end. and reduce the loop count from Heapify Array. So that elements gets sorted from right.
while (sortedElementIndex >= 0) // (N - 1) to 1
{
// Swap the elements (root(maximum value)) of the heap with the last element of the heap
Swap(ref listToSort[0], ref listToSort[sortedElementIndex]);
// sortedElementIndex-- : Decrease the size of the heap by one so that the previous max value will stay in its proper placement
sortedElementIndex--;
if (sortedElementIndex == -1) break;
// Since largest elemented from 0 to last, Re Heapify and get the remaining largest element and place it in 0 position.
Heapify(listToSort, 0, (sortedElementIndex)); // 0 to (N - 1)
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
}
//Heapify() function maintain the heap property (Max Heap or Min Heap). Can be recursive or can use iteration loop like while/for.
void Heapify(int[] listToSort, int parentIndext, int lastChildIndext)
{
//bool doneFlag = false;
int largestElementIndex = 0;
int leftChildIndex = parentIndext * 2 + 1;
int rightChildIndex = parentIndext * 2 + 2;
while (leftChildIndex <= lastChildIndext) //&& !doneFlag)
{
// If leftChild is larger than rightChild or it is the last child and there is no rightChild for this parent.
// Then consider leftChild as largestElement else consider rightChild as largestElement.
if (leftChildIndex == lastChildIndext || listToSort[leftChildIndex] > listToSort[rightChildIndex])
{
largestElementIndex = leftChildIndex;
}
else
{
largestElementIndex = rightChildIndex;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// If largestElement is larger than parent then swap them and make parent as largestElement to continue the loop.
if (listToSort[parentIndext] < listToSort[largestElementIndex])
{
// Make largestElement as parent. And continue finding if childs (left and right) are bigger than element in largestIndex position.
Swap(ref listToSort[parentIndext], ref listToSort[largestElementIndex]);
// Repeat to continue sifting down the child now
parentIndext = largestElementIndex;
leftChildIndex = ((parentIndext * 2) + 1);
rightChildIndex = ((parentIndext * 2) + 2);
}
else
{
//doneFlag = true;
break; // Trying to avoid extra flag condition check. Or return.
}
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
void Swap(ref int num1, ref int num2)
{
int temp = num1;
num1 = num2;
num2 = temp;
}

Algorithm to find added/removed elements in an array

I am looking for the most efficent way of solving the following
Problem:
given an array Before = { 8, 7, 2, 1} and an array After ={1, 3, 8, 8}
find the added and the removed elements
the solution is:
added = 3, 8
removed = 7, 2
My idea so far is:
for i = 0 .. B.Lenghtt-1
{
for j= 0 .. A.Lenght-1
{
if A[j] == B[i]
A[j] = 0;
B[i] = 0;
break;
}
}
// B elemnts different from 0 are the Removed elements
// A elemnts different from 0 are the Added elemnts
Does anyone know a better solution perhaps more efficent and that doesn't overwrite the original arrays
Sorting is your friend.
Sort the two arrays (a and b), and then walk them (using x and y as counters). Move down both 1 at a time. You can derive all your tests from there:
if a[x] < b[y], then a[x] was removed (and only increment x)
if a[x] > b[y], then b[y] was added (and only increment y)
(I may have missed an edge case, but you get the general idea.)
(edit: the primary edge case that isn't covered here is handling when you reach the end of one of the arrays before the other, but it's not hard to figure out. :)
You could also use a Dictionary<int, int> and a algorithm similar to this:
foreach i in source_list: dictionary[i]++;
foreach i in dest_list: dictionary[i]--;
The final dictionary tells you which elements were inserted/removed (and how often). This solution should be quite fast even for bigger lists - faster than sorting.
if your language as multiset available (set with count of elements) your question is a standard operation.
B = multiset(Before)
A = multiset(After)
result is A.symdiff(B) (symdiff is union minus intersection and that is exactly what you are looking for to have removed and added).
Obviously you can also get removed only or added only using classical difference between sets.
It can trivially be implemented using hashes and it's O(n) (using sort is slightly less efficient as it is O(n.log(n)) because of the sort itself).
In some sort of C++ pseudo code:
Before.sort();
After.sort();
int i = 0;
int j = 0;
for (; i < Before.size() && j < After.size(); ) {
if (Before[i] < After[j]) {
Removed.add(Before[i]);
++i;
continue;
}
if (Before[i] > After[j]) {
Added.add(After[j]);
++j;
continue;
}
++i;
++j;
}
for (; i < Before.size(); ++i) {
Removed.add(Before[i]);
}
for (; j < After.size(); ++j) {
Added.add(After[j]);
}
This can be solved in linear time.
Create a map for calculating the number of repetitions of each element.
Go through the before array and fill the map.
Go through the after array and decrease the value in the map for each element.
At the end, go through the map and if you find a negative value, that element was added - if positive, that element was removed.
Here is some Java code for this (not tested, just written right now):
Map<Integer, Integer> repetitionMap = new HashMap<Integer, Integer>();
for (int i = 0; i < before.length; i++) {
Integer number = repetitionMap.get(before[i]);
if (number == null) {
repetitionMap.put(before[i], 1);
} else {
repetitionMap.put(before[i], number + 1);
}
}
for (int i = 0; i < after.length; i++) {
Integer number = repetitionMap.get(after[i]);
if (number == null) {
repetitionMap.put(after[i], -1);
} else {
repetitionMap.put(after[i], number - 1);
}
}
Set<Integer> keySet = repetitionMap.keySet();
for (Integer i : keySet) {
Integer number = repetitionMap.get(i);
if (number > 0) {
System.out.println("removed " + number + "times value " + i);
}
if (number < 0) {
System.out.println("added " + number + "times value " + i);
}
}
perl:
#a = ( 8, 7, 2, 2, 1 );
#b = ( 1, 3, 8, 8, 8 );
$d{$_}++ for(#a);
$d{$_}-- for(#b);
print"added = ";
for(keys %d){print "$_ " x (-$d{$_}) if($d{$_}<0)}
print"\n";
print"removed = ";
for(keys %d){print "$_ " x ($d{$_}) if($d{$_}>0)}
print"\n";
result:
$ ./inout.pl
added = 8 8 3
removed = 7 2 2
In Groovy:
def before = [8, 7, 2, 1, 1, 1], after = [1, 3, 8, 8, 8]
def added = before.countBy{it}
def result = after.inject(added){map, a -> map[a] ? map << [(a):map[a] - 1]: map << [(a):-1]}
.inject([:]){m, k, v -> v == 0 ? (m << [:]) : (v < 0 ? m << [(k):"added ${v.abs()} times"] : m << [(k):"removed ${v.abs()} times"])}
println "before $before\nafter $after"
println "result: $result"
Result:
before [8, 7, 2, 1, 1, 1]
after [1, 3, 8, 8, 8]
result: [8:added 2 times, 7:removed 1 times, 2:removed 1 times, 1:removed 2 times, 3:added 1 times]
For countBy I got insipred from Some groovy magic post
In groovy inject is like reduce in other functional languages.
I also refer Groovy collection api slides from Trygve Amundsen with really good table with functional methods
Second solution:
def before = [8, 7, 2, 1, 1, 1], after = [1, 3, 8, 8, 8]
def sb = before.countBy{it}
def sa = after.countBy{it}
def result = sa.inject(sb){m, k, v -> m[k] ? m << [(k): m[k] - v] : m << [(k): -v]}
.inject([:]){m, k, v -> v == 0 ? (m << [:]) : (v < 0 ? m << [(k):"added ${v.abs()} times"] : m << [(k):"removed ${v.abs()} times"])}

Resources