Scoping of variables in a for loop - loops

The following program manipulates two arrays. The algorithm is of little importance except to note that the array tmp gradually gets smaller.
package main
import (
"strings"
"fmt"
)
func arrayToString(a []int, delim string) string {
return strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")
}
func numSeries(arr []int) ([]int, []int) {
return arr[:2], arr[2:]
}
func main() {
highnum := []int{8,7}
tmp := []int{6,5,4,3,2,1}
curr := []int{}
// fRight := ""
for i, v := range highnum {
curr, tmp = numSeries(tmp)
fmt.Printf("v %d numSeries result curr %s tmp %s\n", v, arrayToString(curr, ", " ), arrayToString(tmp, ", " ) )
fmt.Printf("final (1) %s\n", arrayToString(tmp, ", " ) )
// fRight += "[" + arrayToString(append(curr, v), ", ") + "]"
if i == len(highnum)-1 {
fmt.Printf("final (2) %s\n", arrayToString(tmp, ", " ) )
}
// fmt.Printf("fRight |%s|\n", fRight)
}
}
The results are:
v 8 numSeries result curr 6, 5 tmp 4, 3, 2, 1
final (1) 4, 3, 2, 1
v 7 numSeries result curr 4, 3 tmp 2, 1
final (1) 2, 1
final (2) 2, 1
The program works - final (1) and final (2) comments in the output have the same value. However, if I uncomment the three commented-out statements above I get an incorrect result. Here is the incorrect program and the result.
package main
import (
"strings"
"fmt"
)
func arrayToString(a []int, delim string) string {
return strings.Trim(strings.Replace(fmt.Sprint(a), " ", delim, -1), "[]")
}
func numSeries(arr []int) ([]int, []int) {
return arr[:2], arr[2:]
}
func main() {
highnum := []int{8,7}
tmp := []int{6,5,4,3,2,1}
curr := []int{}
fRight := ""
for i, v := range highnum {
curr, tmp = numSeries(tmp)
fmt.Printf("v %d numSeries result curr %s tmp %s\n", v, arrayToString(curr, ", " ), arrayToString(tmp, ", " ) )
fmt.Printf("final (1) %s\n", arrayToString(tmp, ", " ) )
fRight += "[" + arrayToString(append(curr, v), ", ") + "]"
if i == len(highnum)-1 {
fmt.Printf("final (2) %s\n", arrayToString(tmp, ", " ) )
}
fmt.Printf("fRight |%s|\n", fRight)
}
}
Result:
v 8 numSeries result curr 6, 5 tmp 4, 3, 2, 1
final (1) 4, 3, 2, 1
fRight |[6, 5, 8]|
v 7 numSeries result curr 8, 3 tmp 2, 1
final (1) 2, 1
final (2) 7, 1
fRight |[6, 5, 8][8, 3, 7]|
Note that final (1) and final (2) comments have different values. This is wrong. I suspect this type of behaviour is entirely correct and I am falling for a beginner's 'gotcha'. I'd be grateful if somebody could point out an existing StackOverflow question that gives a solution, or describes what is going on. I'd be especially happy to see a reference to the official language definition covering this issue.

It is here:
fRight += "[" + arrayToString(append(curr, v), ", ") + "]"
In particular, append. The key here is that slices are views on arrays. The curr and tmp point to the same underlying array, curr being the beginning part of it and tmp being the end. The way append works is that if the slice has sufficient capacity, it will simply add the element to the end of the slice and increase the len. When you run that append, curr has sufficient capacity, so v is added to the end of curr, which happends to be the first element of tmp. So you overwrite that element.

Related

How to sort an array

I have problem for sorting my array in Go,
Here is my code :
func main() {
fmt.Print("Masukkan Jumlah Data yang akan dimasukkan: ")
var jumlahdata int
fmt.Scanln(&jumlahdata)
var DataDiagram = make([]int, jumlahdata)
fmt.Print("Masukkan data secara berurutan dengan spasi sebagai pemisah antar angka: ")
for i := 0; i < jumlahdata; i++ {
fmt.Scanf("%d", &DataDiagram[i])
}
fmt.Print("\n")
var max int = DataDiagram[0]
for _, value := range DataDiagram { // Menemukan nilai maximum
if value > max {
max = value
}
}
var mem int
Sorting(DataDiagram, jumlahdata, mem, max)
}
func Grafik(jumlahdata int, max int, DataDiagram []int) {
for i := max; i >= 1; i-- { // membuat Data Diagram
for j := 0; j < jumlahdata; j++ {
if DataDiagram[j] >= i {
fmt.Print(" | ")
} else {
fmt.Print(" ")
}
}
fmt.Print("\n")
}
for i := 0; i < jumlahdata; i++ {
fmt.Print("---")
}
fmt.Print("\n")
fmt.Print(" ")
for i := 0; i < jumlahdata; i++ {
fmt.Print(DataDiagram[i], " ")
}
}
func Sorting(DataDiagram []int, jumlahdata int, mem int, max int) {
for langkah := 0; langkah < (jumlahdata-1) ; langkah++ {
Grafik(jumlahdata, max, DataDiagram)
for i := 0; i < jumlahdata - (langkah-1); i++ {
if DataDiagram[i] > DataDiagram[i + 1] {
mem := DataDiagram[i];
DataDiagram[i] = DataDiagram[i + 1]
DataDiagram[i + 1] = mem;
}
}
}
}
What i expect is look like this:
What I Expect
But the output said otherwise, it give me error : It give me error
Can someone give some guide how to fix this :) i just learn Go yesterday, it similiar to C, but keep giving me index out of range error
I understand your task is to sort an int "array" (slice, in go-speak), showing each step of your work as a graph. Because you must show your work, you can't use go's built-in sorting, e.g. sort.Ints(DataDiagram).
Your problems are with the Sorting function.
Step 1 Your immediate crash-causing problem is that i eventually iterates to a number larger than upper index of DataDiagram. That we fix in the commented line below.
// Step 1: fix the iterator
func Sorting(DataDiagram []int, jumlahdata int, mem int, max int) {
for langkah := 0; langkah < (jumlahdata-1) ; langkah++ {
Grafik(jumlahdata, max, DataDiagram)
for i := 0; i < jumlahdata - 1; i++ { // Was: for i := 0; i < jumlahdata - (langkah-1); i++ {
if DataDiagram[i] > DataDiagram[i + 1] {
mem := DataDiagram[i];
DataDiagram[i] = DataDiagram[i + 1]
DataDiagram[i + 1] = mem;
}
}
}
}
Step 2 The code no longer crashes, but is not guaranteed to sort, because it makes only one pass through the inputs. We need to continue looping until there's no more swapping taking place. That problem is fixed below. The code now produces the expected output on the playground.
// Step 2: loop until sorted
func Sorting(DataDiagram []int, jumlahdata int, mem int, max int) {
swapped := true
for swapped {
Grafik(jumlahdata, max, DataDiagram)
swapped = false
for i := 0; i < jumlahdata - 1; i++ {
if DataDiagram[i] > DataDiagram[i + 1] {
mem := DataDiagram[i];
DataDiagram[i] = DataDiagram[i + 1]
DataDiagram[i + 1] = mem;
swapped = true
}
}
}
}
Step 3 The above code works fine, but perhaps can use some tidying. The end result is unchanged on the playground.
// Step 3: make it prettier
func Sorting(data []int) {
max := data[0]
for _, value := range data { // Menemukan nilai maximum
if value > max {
max = value
}
}
swapped := true
for swapped {
Grafik(len(data), max, data)
swapped = false
for i := 0; i < len(data)-1; i++ {
if data[i] > data[i+1] {
data[i], data[i+1] = data[i+1], data[i]
swapped = true
}
}
}
}
It's much simpler if you would just use
sort.Ints(ints), which you can see here:
https://goplay.space/#i9VIrDG-vL-

Finding the sum of nearest smallest and greatest number in an array

The question is straightforward that we want to find the sum of the nearest smallest and greatest number for every index of an array. But the part which I am not able to get is how to return the resulting array with the sum values on the same index values as the numbers.
Example: [1,5,2,3,8]
The sum for each indexes is as follows:
for index 0 the element is 1 hence sum = (0 + 2) = 2
since it has no smallest number therefore taking the nearest smallest number to be 0.
similarly for index 1 sum = (3 + 8 ) = 11 {3 - nearest smallest number to 5 and 8 nearest largest number}
and so on.
What I did was sort the given array then iterate through it and in every iteration take the sum of arr[i-1] + arr[i+1] elements and storing them in the result/answer array.{ with 0th and last element being dealt with separately }
So basically
If the input array is -> [1,5,2,3,8]
the resultant array will be -> [2,4,7,11,5]
but it is required to be as -> [2,11,4,7,5]
that is the sum of each index element to be at the same index as was the initial number
(I am using C++)
You will get the desired output by running this. It is written in Go.
Copy the original input array
Sort the copied array
Create a Map Sorted Value -> Sorted Index
make an output array with same length as input
Iterate through the Input Array and check the value is largest or smallest and Do the Sum as described in the question
store the Sum in the output array in the same index as input array
package main
import (
"fmt"
"sort"
)
func main() {
indexes := []int{1, 5, 2, 3, 8}
output := findSum(indexes)
fmt.Println(output)
}
func findSum(input []int) []int {
if len(input) < 2 {
return input
}
sorted := make([]int, len(input))
copy(sorted, input)
sort.Slice(sorted, func(i, j int) bool {
return sorted[i]< sorted[j]
})
sortedMap := make(map[int]int)
for i, i2 := range sorted {
sortedMap[i2] = i
}
output := make([]int, len(input))
for j, index := range input {
i := sortedMap[index]
if i == 0 {
output[j] = sorted[i+1]
continue
}
if i == len(input) - 1 {
output[j] = sorted[i-1]
continue
}
output[j] = sorted[i - 1] + sorted[i + 1]
}
return output
}
You can run here
The trick is to create an array of indexes into the original array and sort this, rather than sorting the original array.
You don't mention what language you're using, but here's some Java code to illustrate:
Integer[] arr = {1,5,2,3,8};
System.out.println(Arrays.toString(arr));
Integer[] idx = new Integer[arr.length];
for(int i=0; i<idx.length; i++) idx[i] = i;
System.out.println(Arrays.toString(idx));
Arrays.sort(idx, (a, b) -> arr[a] - arr[b]);
System.out.println(Arrays.toString(idx));
Integer[] res = new Integer[arr.length];
for(int i=0; i<arr.length; i++)
{
res[idx[i]] = (i > 0 ? arr[idx[i-1]] : 0) + (i < arr.length - 1 ? arr[idx[i+1]] : 0);
}
System.out.println(Arrays.toString(res));
Or translated into (possibly non-idiomatic) C++ (Ideone):
int len = 5;
array<int, 5> arr{1,5,2,3,8};
for(auto i : arr) cout << i << " ";
cout << endl;
array<int, 5> idx;
for(int i=0; i<len; i++) idx[i] = i;
for(auto i : idx) cout << i << " ";
cout << endl;
sort(idx.begin(), idx.end(), [&arr](int a, int b) {return arr[a] < arr[b];});
for(auto i : idx) cout << i << " ";
cout << endl;
array<int, 5> res;
for(int i=0; i<len; i++)
res[idx[i]] = (i > 0 ? arr[idx[i-1]] : 0) + (i < len - 1 ? arr[idx[i+1]] : 0);
for(auto i : res) cout << i << " ";
cout << endl;
Output:
[1, 5, 2, 3, 8]
[0, 1, 2, 3, 4]
[0, 2, 3, 1, 4]
[2, 11, 4, 7, 5]

Given an array A of size N, find all combination of four elements in the array whose sum is equal to a given value K

Given an array A of size N, find all combinations of four elements in the array whose sum is equal to a given value K. For example, if the given array is {10, 2, 3, 4, 5, 9, 7, 8} and K = 23, one of the quadruple is “3 5 7 8” (3 + 5 + 7 + 8 = 23).
The output should contain only unique quadruple For example, if the input array is {1, 1, 1, 1, 1, 1} and K = 4, then the output should be only one quadruple {1, 1, 1, 1}
My approach: I tried to solve this problem by storing all the distinct pairs formed from the given array into a hash table (std::unordered_multimap), with their sum as key. Then for each pair sum, I looked for (K - sum) key in the hash table. The problem with this approach is I am getting too many duplicated like (i, j, l, m) and (i, l, j, m) are the same, plus there are duplicates due to the same items in the array. I am not sure what is the optimal way to address that.
The code for the above-mentioned approach is:
#include <iostream>
#include <unordered_map>
#include <tuple>
#include <vector>
int main() {
size_t tc = 0;
std::cin >> tc; //number of test cases
while(tc--) {
size_t n = 0, k = 0;
std::cin >> n >> k;
std::vector<size_t> vec(n);
for (size_t i = 0; i < n; ++i)
std::cin >> vec[i];
std::unordered_multimap<size_t, std::tuple<size_t, size_t>> m;
for (size_t i = 0; i < n - 1; ++i)
for (size_t j = i + 1; j < n; ++j) {
const auto sum = vec[i] + vec[j];
m.emplace(sum, std::make_tuple(i, j));
}
for (size_t i = 0; i < n - 1; ++i)
for (size_t j = i + 1; j < n; ++j) {
const auto sum = vec[i] + vec[j];
auto r = m.equal_range(k - sum);
for (auto it = r.first; it != r.second; ++it) {
if ((i == std::get<0>(it->second))
||(i == std::get<1>(it->second))
||(j == std::get<0>(it->second))
|| (j == std::get<1>(it->second)))
continue;
std::cout << vec[i] << ' ' << vec[j] << ' '
<< vec[std::get<0>(it->second)] << ' '
<< vec[std::get<1>(it->second)] << '$';
}
r = m.equal_range(sum);
for (auto it = r.first; it != r.second; ++it) {
if ((i == std::get<0>(it->second))
&& (j == std::get<1>(it->second))) {
m.erase(it);
break;
}
}
}
std::cout << '\n';
}
return 0;
}
The above code will run as-is in the link mentioned below in the Note.
Note: This problem is taken from https://practice.geeksforgeeks.org/problems/find-all-four-sum-numbers/0
To handle duplicate values in array
Consider [2, 2, 2, 3, 3] with goal 10.
The only solution is the 4-tuple <2,2,3,3>. The main point is to avoid choosing two 2 among three 2.
Let's consider the k-class, the set of tuples in which every tuple contain only k.
e.g: in our array we have the 2-class and 3-class.
The 2-class contains:
<2>
<2,2>
<2,2,2>
while the 3-class contains:
<3>
<3,3>
An idea is to reduce the array of elem (elem being an integer value) to an array of k-class.
idem
[[<2>, <2,2>, <2,2,2>], [<3>, <3,3>]]
We can think of taking the cartesian product between the 2-class set and the 3-class set, and check which result lead to solution.
More concretely, let's take some tuple T, whose last (==rightmost) value is k. (in <2,3,4> rightmost value would be 4).
We can pick any l-class from our array (* l > k) and join the tuples from that l-class to T.
e.g
consider array [2, 9, 9, 3, 3, 4, 6] and tuple <2, 3, 3>
The rightmost value is 3.
The candidate k-class are 4-class, 6-class, 9-class
we can join:
<4>
<6>
<9>
<9,9>
so the next candidates will be:
<2, 3, 3, 4>
<2, 3, 3, 6>
<2, 3, 3, 9>
<2, 3, 3, 9, 9> //that one has too many elem, left for illustration
(* The purpose of l > k is to prevent the permutation. (if <1,2> is solution you don't want <2,1> since addition is commutative))
Algorithm
Foreach tuple, try to create new ones by rightjoining tuples from a "greater" k-class.
discard the resulting ones which have too many elements or whose sum is already too big...
At some point we won't have new candidates, so algorithm will stop
example of cuts:
given array [2,3,7,8,10,11], and tuple <2,3> and S == 13
<2,3,7> is candidate (2+3+7 = 12 < 13)
<2,3,10> is not candidate (2+3+10 = 15 > 13)
<2,3,11> even more so. not
<2,3,8> is not candidate either since the next rightjoin (to reach a 4-tuple) will overflow S
given array [2,3,4,4,4] given tuple <2,3> and candidate <4,4,4>
resulting tuple would be <2,3,4,4,4> which has too many elems, discard it!
Obviously the initialization is
some empty tuple
whose sum is 0
and whose rightmost element is less than any k from the array (you can rightjoin it anybody)
I believe it should not be too hard to translate to C++
class TupleClass {
sum = 0
rightIdx = -1
values = [] // an array of integers (hopefully summing to solution)
//idx is the position of the k-class found in array
add (val, idx) {
const t = new TupleClass()
t.values = this.values.concat(val)
t.sum = this.sum + val
t.rightIdx = idx
return t;
}
toString () {
return `<${this.values.join(',')}>`
}
addTuple (tuple, idx) {
const t = new TupleClass
t.values = this.values.concat(tuple.values)
t.sum = this.sum + tuple.sum
t.rightIdx = idx
return t;
}
get size () {
return this.values.length
}
}
function nodupes (v, S) {
v = v.reduce((acc, klass) => {
acc[klass] = (acc[klass] || {duplicity: 0, klass})
acc[klass].duplicity++
return acc
}, {})
v = Object.values(v).sort((a,b) => a.klass - b.klass).map(({ klass, duplicity }, i) => {
return Array(duplicity).fill(0).reduce((acc, _) => {
const t = acc[acc.length-1].add(klass, i)
acc.push(t)
return acc
}, [new TupleClass()]).slice(1)
})
//v is sorted by k-class asc
//each k-class is an array of tuples with increasing length
//[[<2>, <2,2>, <2,2,2>], [<3>,<3,3>]]
let tuples = [new TupleClass()]
const N = v.length
let nextTuples = []
const solutions = []
while (tuples.length) {
tuples.forEach(tuple => {
//foreach kclass after our rightmost value
for (let j = tuple.rightIdx + 1; j <= N - 1; ++j) {
//foreach tuple of that kclass
for (let tclass of v[j]) {
const nextTuple = tuple.addTuple(tclass, j)
if (nextTuple.sum > S || nextTuple.size > 4) {
break
}
//candidate to solution
if (nextTuple.size == 4) {
if (nextTuple.sum === S) {
solutions.push(nextTuple)
}
//invalid sum so adding more elem won't help, do not push
} else {
nextTuples.push(nextTuple)
}
}
}
})
tuples = nextTuples
nextTuples = []
}
return solutions;
}
const v = [1,1,1,1,1,2,2,2,3,3,3,4,0,0]
const S = 7
console.log('v:', v.join(','), 'and S:',S)
console.log(nodupes(v, 7).map(t=>t.toString()).join('\n'))

Sum of squares in an array using recursion in golang

So my friend gave me this task where the sum of squares of positive numbers must be calculated using recursion.
Conditions - The input will be a string with space separated numbers
This is what I've come so far but this shows a runtime error.
Here is the full error https://ideone.com/53oOjN
package main
import(
'fmt',
'strings',
'strconv'
)
var n int = 4
var sum_of_squares int = 0
func sumOfSquares(strArray []string, iterate int) int{
number, _ := strconv.Atoi(strArray[iterate])
if number > 0 {
sum_of_squares += number*number
}
if iterate == n {
return 0 // just to end the recursion
}
return sumOfSquares(strArray, iterate+1)
}
func main() {
str := "1 2 3 4"
strArray := strings.Fields(str)
result := sumOfSquares(strArray, 0)
fmt.Println(sum_of_squares, result)
}
The rule of thumb in recursion is termination condition. It should exist and it should exist in the right place.
func sumOfSquares(strArray []string, iterate int) int{
if iterate >= len(strArray) {
return sum_of_squares
}
number, _ := strconv.Atoi(strArray[iterate]) //TODO: handle err here
sum_of_squares += number*number
return sumOfSquares(strArray, iterate+1)
}
Just for you information: canonical recursion should not save it's state into global fields. I would suggest using following function signature.
func sumOfSquares(strArray []string, iterate, currentSum int) int{
//...
return sumOfSquares(strArray, iterate+1, sum_of_squares)
}
So that you don't need to store sum_of_squares somewhere. You will just pass it to next function invocation.
package main
import (
"fmt"
"strconv"
"strings"
)
var n int
func sumOfSquares(strArray []string, iterate int) int {
number, _ := strconv.Atoi(strArray[iterate])
if iterate == n {
return number * number
}
return ((number * number) + sumOfSquares(strArray, iterate+1))
}
func main() {
str := "1 2 3 4"
strArray := strings.Fields(str)
n = len(strArray) - 1
result := sumOfSquares(strArray, 0)
fmt.Println(result)
}
Indexing starts from 0, so decrease the length by one.
As #peterSO have pointed out, if strings contain unusual characters, it doesn't work, I didn't post the right answer for getting input because you seem to be beginner, but you can read the input, like this instead.
var inp []byte
var loc int
inp, _ = ioutil.ReadFile(fileName)
//add \n so that we don't end up running out of bounds,
//if last byte is integer.
inp = append(inp, '\n')
func scanInt() (res int) {
if loc < len(inp) {
for ; inp[loc] < 48 || inp[loc] > 57; loc++ {
}
for ; inp[loc] > 47 && inp[loc] < 58; loc++ {
res = res<<3 + res<<1 + (int(inp[loc]) - 48)
}
}
return
}
This is faster and scans integers only, and skips all other unusual characters.
I like to keep it simple. I have some few if conditions as well, but hope you like it.
func sumOfSquares(numArr []string) int {
i, err := strconv.Atoi(numArr[0])
rest := numArr[1:]
//Error checking
if err != nil {
fmt.Println(err)
os.Exit(1)
return 0
}
square := i * i
// negative & last number
if i < 0 && len(rest) == 0 {
return square
}
// negative & not last number
if i < 0 && len(rest) > 0 {
return sumOfSquares(rest)
}
// last man standing
if i >= 0 && len(rest) == 0 {
return square
}
return square + sumOfSquares(rest)
}
DEMO : https://play.golang.org/p/WWYxKbvzanJ

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