I am doing the following programming exercise: Sort an array by value and index. The statement is:
Your task is to sort an array of integer numbers by the product of the value and the index of the positions.
For sorting the index starts at 1, NOT at 0! The sorting has to be ascending. The array will never be null and will always contain numbers.
Example:
Input: 23, 2, 3, 4, 5 Product of value and index: 23 => 23 * 1 = 23 -> Output-Pos 4 2 => 2 * 2 = 4 -> Output-Pos 1 3 => 3 * 3 = 9 -> Output-Pos 2 4 => 4 * 4 = 16 -> Output-Pos 3 5 => 5 * 5 = 25 -> Output-Pos 5
Output: 2, 3, 4, 23, 5
I have tried to use a map where we store the original array's values as keys, and the product of the original ones with its indexes, are the values:
import java.util.*;
import java.util.stream.*;
public class Kata
{
public static int[] sortByValueAndIndex/**/(int[] array)
{
System.out.println("\nArray: "+Arrays.toString(array));
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0; i < array.length; i++){
map.put(array[i],array[i]*(i+1));
}
System.out.println("map: "+map.toString());
map = map.entrySet().stream().sorted(Map.Entry.comparingByValue())
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
(e1, e2) -> e1, LinkedHashMap::new));
System.out.println("sorted map: "+map.toString());
return map.keySet().stream().mapToInt(Number::intValue).toArray();
}
}
It does work for arrays with unique elements, for example for the following tests(extracted from the exercise):
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;
import java.util.Arrays;
public class KataTests {
@Test
public void exampleTests() {
int[] actual = Kata.sortByValueAndIndex(new int[] { 1, 2, 3, 4, 5 });
int[] expected = new int[] { 1, 2, 3, 4, 5 };
String message = "Your result:\n" + arrayToString(actual) + "\n\nExpected result:\n" + arrayToString(expected) + "\n\n";
assertEquals(message, arrayToString(expected), arrayToString(actual));
actual = Kata.sortByValueAndIndex(new int[] { 23, 2, 3, 4, 5 });
expected = new int[] { 2, 3, 4, 23, 5 };
message = "Your result:\n" + arrayToString(actual) + "\n\nExpected result:\n" + arrayToString(expected) + "\n\n";
assertEquals(message, arrayToString(expected), arrayToString(actual));
actual = Kata.sortByValueAndIndex(new int[] { 26, 2, 3, 4, 5 });
expected = new int[] { 2, 3, 4, 5, 26 };
message = "Your result:\n" + arrayToString(actual) + "\n\nExpected result:\n" + arrayToString(expected) + "\n\n";
assertEquals(message, arrayToString(expected), arrayToString(actual));
actual = Kata.sortByValueAndIndex(new int[] { 9, 5, 1, 4, 3 });
expected = new int[] { 1, 9, 5, 3, 4 };
message = "Your result:\n" + arrayToString(actual) + "\n\nExpected result:\n" + arrayToString(expected) + "\n\n";
assertEquals(message, arrayToString(expected), arrayToString(actual));
}
private String arrayToString(int[] array)
{
return Arrays.toString(array);
}
}
However, when we have as input an array with repeated elements, it raises an exception because of map's keys are uniques so we are omitting some original array's numbers.
For example if input is:
[7, -9, 24, 0, 7, 23, -4, -28, -14, 5, 20, 26, 22, -24]
Current output:
[-24, -28, -14, -4, -9, 0, 7, 5, 24, 23, 20, 22, 26]
Expected one:
[-24, -28, -14, -4, -9, 0, 7, 7, 5, 24, 23, 20, 22, 26]
As we see, using the trace, we observe that the map holds just the product for the last 7, 7*5=35:
Array: [7, -9, 24, 0, 7, 23, -4, -28, -14, 5, 20, 26, 22, -24]
map: {0=0, -4=-28, 5=50, 7=35, -9=-18, -14=-126, 20=220, 22=286, 23=138, -24=-336, 24=72, 26=312, -28=-224}
sorted map: {-24=-336, -28=-224, -14=-126, -4=-28, -9=-18, 0=0, 7=35, 5=50, 24=72, 23=138, 20=220, 22=286, 26=312}
Is there a way to fix this behaviour to allow repeated elements? Do we need to use another data structure other than map? Are there other approaches, like Comparator / Compare, or just using Lists/Arrays?‽
I have also read:
- custom sorting a java array
- Using Lambda expression, I want to sort by integer values using the Java language
- Java 8 Stream and operation on arrays
- Store an array in HashMap
- Sort a Map<Key, Value> by values
- Get keys from HashMap in Java
- How can I convert a Java HashSet<Integer> to a primitive int array?
- Java Sorting: sort an array of objects by property, object not allowed to use Comparable
- How to override toString() properly in Java?
EDIT: As @JB Nizet recommended we can do the following:
import java.util.*;
import java.util.stream.*;
public class Kata
{
public static int[] sortByValueAndIndex/**/(int[] array)
{
ElementWithProduct[] elements = new ElementWithProduct[array.length];
for(int i = 0; i < elements.length; i++){
elements[i] = new ElementWithProduct(array[i], array[i]*(i+1));
}
Arrays.sort(elements, new Comparator<ElementWithProduct>(){
public int compare(ElementWithProduct e1, ElementWithProduct e2){
if(e1.product > e2.product){
return 1;
}
if(e1.product < e2.product){
return -1;
}
return 0;
}
});
for(int i = 0; i < elements.length; i++){
array[i] = elements[i].value;
}
return array;
}
public static class ElementWithProduct{
public int value;
public int product;
public ElementWithProduct(int value, int product){
this.value = value;
this.product = product;
}
}
}