I know how to find the highest value and index in a array(list). But I dont know how to get the indexes if there are multiple highest values in a array. I want to create a method/function that can two things: fill the array(list) with only one index if there is only one highest value, or create a arraylist if there are multiple highest values. For example I give two array's:
Array1={42,3,42,42,42,5,8};
I want to get the all the indexes of value 42 in a new array(list).
Array2={42,3,35,67};
I want to create a array(list) with only one index of value 42.
Try this for multiple indexes
List<Integer> list = new ArrayList<>();
int array[] = {1,1,2,4,5,3,1,5};
int max = array[0];
list.add(0);
for(int i=1;i<array.length;i++){
if(max<array[i]){
max = array[i];
list.clear();
list.add(i);
}else if(max==array[i])
list.add(i);
}
System.out.println(list);
For single index, use an extra variable, to store it it.
Using Java 8 features and assuming the array is not empty:
int maxValue = Arrays.stream(array)
.max()
.getAsInt();
int[] maxIndexes = IntStream.range(0, array.length)
.filter(i -> array[i] == maxValue)
.toArray();
That's 2 iterations where first you find the max value and then the indexes where an array element is equal to the max value.
Some documentation if you are not familiar with some classes/methods above:
IntStream, toArray(), getAsInt()
Depending on your scenario, having a small data set or a large data set, you might want to process the items sequentially or in parallel.
NOTE: the following code contains JUnit #Test annotation and AssertJ assertions.
Solution: sequential, one pass, small data set
This solution parses the array and keeps track of maximum and current maximum indexes. If a new maximum is found the indexes are cleared and the new maximum indexes are inserted.
#Test
public void sequential_algorithm_return_max_with_indexes() {
int[] values = new int[]{42, 3, 42, 42, 42, 5, 8};
int maxValue = Integer.MIN_VALUE;
List<Integer> maxValueIndexes = new ArrayList<>();
for (int index = 0; index < values.length; index++) {
int value = values[index];
if (value == maxValue) {
maxValueIndexes.add(index);
} else {
if (value > maxValue) {
maxValue = value;
maxValueIndexes.clear();
maxValueIndexes.add(index);
}
}
}
assertThat(maxValue).isEqualTo(42);
assertThat(maxValueIndexes).containsExactly(0, 2, 3, 4);
}
Solution: parallel, large data set
Streams are flexible and allow parallel processing.
Bellow data is represented as a pair of index-value instead of an array. This is done in order to transform the array of pairs into a stream and keep track of indexes.
Because this supposed to work in parallel, reduce method accepts 3 arguments - initial value, accumulator and combiner. This means that multiple buckets run in parallel. For each bucket there is an initial value and an accumulator used to process items sequentially. Then the parallel results of buckets are combined using the combiner argument.
#Test
public void parallel_algorithm_return_max_with_indexes() {
Pair<Integer, Integer>[] values = new Pair[]{
new Pair<>(0, 42),
new Pair<>(1, 3),
new Pair<>(2, 42),
new Pair<>(3, 42),
new Pair<>(4, 42),
new Pair<>(5, 5),
new Pair<>(6, 8),
};
ValueIndexes<Integer> maxValueIndexes = Arrays.stream(values)
.parallel()
.reduce(
new ValueIndexes<>(Integer.MIN_VALUE),
(ValueIndexes<Integer> valueIndexes, Pair<Integer, Integer> value) -> {
if (valueIndexes.getValue() == value.getValue()) {
valueIndexes.addIndex(value.getKey());
} else {
if (value.getValue() > valueIndexes.getValue()) {
valueIndexes = new ValueIndexes<>(value.getValue());
valueIndexes.addIndex(value.getKey());
}
}
return valueIndexes;
},
(valueIndexes1, valueIndexes2) -> {
if (valueIndexes1.getValue() == valueIndexes2.getValue()) {
ValueIndexes<Integer> valueIndexes = new ValueIndexes<>(valueIndexes1.getValue());
valueIndexes.addIndexes(valueIndexes1.getIndexes());
valueIndexes.addIndexes(valueIndexes2.getIndexes());
return valueIndexes;
} else {
if (valueIndexes1.getValue() > valueIndexes2.getValue()) {
return valueIndexes1;
} else {
return valueIndexes2;
}
}
}
);
assertThat(maxValueIndexes.getValue()).isEqualTo(42);
assertThat(maxValueIndexes.getIndexes()).containsExactlyInAnyOrder(0, 2, 3, 4);
}
private class ValueIndexes<T> {
private T value;
private List<Integer> indexes = new ArrayList<>();
public ValueIndexes(T value) {
this.value = value;
}
public T getValue() {
return value;
}
public Iterable<Integer> getIndexes() {
return indexes;
}
public void addIndexes(Iterable<Integer> indexes) {
indexes.forEach(this::addIndex);
}
public void addIndex(int index) {
indexes.add(index);
}
}
Related
I have a function that gets an array of primitive data (int), I need to return the array of two elements the smallest and the largest number. If length of the array is 1 then just return two first elements int the array. I came up with such solution:
public static int[] minMax(int[] arr) {
if (arr.length==1)
return new int[]{arr[0],arr[0]} ;// return if arr is 1 element
else {
ArrayList<Integer> ar = new ArrayList<Integer>();
//?
return new int[]{Collections.max(ar),Collections.min(ar)};
}
}
But how do I convert an array to an ArrayList? Is there a more efficient way maybe?
By calling Collections.min and Collections.max, you're iterating over the array twice. You can cut this time down by streaming the array and letting it do the heavy lifting, as James Mudd suggested. However, note that by doing so you'd be wasting time on calculating the sum and accumulating the count of the elements, which you don't care about. It may be more efficient to calculated these yourself:
public static int[] minMax(int[] arr) {
// Assuming arr has at least one element
int min = arr[0];
int max = arr[0];
for (int i = 1; i < arr.length; ++i) {
int curr = arr[i];
if (curr < min) {
min = curr;
} else if (curr > max) {
max = curr;
}
}
return new int[]{min, max};
}
You could use IntSummaryStatistics the method would look like
public static int[] minMax(int[] arr) {
IntSummaryStatistics intSummaryStatistics = Arrays.stream(arr).summaryStatistics();
return new int[] {intSummaryStatistics.getMin(), intSummaryStatistics.getMax()};
}
Here's another way of solving this task with streams.
Instead, using IntSummuryStatistics we can provide an array of two elements as a container of the resulting values while applying collect and manually define to accumulate stream elements in it.
public static int[] minMax(int[] sourceArr) {
if (sourceArr.length == 0) throw new IllegalArgumentException(); // source array is empty
return Arrays.stream(sourceArr)
.collect(
() -> new int[]{sourceArr[0], sourceArr[0]}, // mutable container
(arr, next) -> { // accumulator - populating the container with elements of the stream
arr[0] = Math.min(arr[0], next);
arr[1] = Math.max(arr[1], next);
},
(left, right) -> { // combiner - merging containers in parallel
left[0] = Math.min(left[0], right[0]);
left[1] = Math.max(left[1], right[1]);
});
}
main()
public static int[] minMax(int[] sourceArr) {
System.out.println(Arrays.toString(minMax(new int[]{3, 5, -3, 8, 9})));
System.out.println(Arrays.toString(minMax(new int[]{3, -1, 3, 12, 27})));
}
Output:
[-3, 9]
[-1, 27]
But how do I convert an array to an ArrayList?
It's not possible to convert an array of primitives into a List directly. When you have an array of reference type (like Integer, BigDecimal, etc.) you can use Arrays.asList() to fixed-size list wrapped around the given array. But it wouldn't work with primitive arrays.
In order to translate int[] into a List<Integer> you have to create a new list and populate it with the contents of the array.
So your actual goal is to obtain the result as a list, the code provided above might be changed like that:
public static List<Integer> minMax(int[] sourceArr) {
if (sourceArr.length == 0) throw new IllegalArgumentException(); // source array is empty
return Arrays.stream(sourceArr)
.collect(
() -> Arrays.asList(sourceArr[0], sourceArr[0]), // mutable container
(list, next) -> { // accumulator - populating the container with elements of the stream
list.set(0, Math.min(list.get(0), next));
list.set(1, Math.max(list.get(1), next));
},
(left, right) -> { // combiner - merging containers in parallel
left.set(0, Math.min(left.get(0), right.get(0)));
left.set(1, Math.max(left.get(1), right.get(1)));
});
}
Assume I have List<List<Integer>> lstOfintLst , where for each List<Integer> intList I have to check multiple conditions and for each condition I have a set of actions ex count of each condition match , adding them in new List<Integer> matchedLstCon_x etc.
New to Java 8 , I am not clear how can I achieve it with just streaming once instead had to steam for each conditions as below
AtomicInteger count_Unmatched= new AtomicInteger();
AtomicInteger count_Con_1= new AtomicInteger();
AtomicInteger count_Con_2= new AtomicInteger();
AtomicInteger count_Con_3= new AtomicInteger();
List<Integer> list_Unmatched = new ArrayList<Integer>();
List<Integer> list_Con_1 = new ArrayList<Integer>();
List<Integer> list_Con_2 = new ArrayList<Integer>();
List<Integer> list_Con_3 = new ArrayList<Integer>();
lstOfintLst.stream().forEach(intList -> {
boolean isUnmatched=true;
if(intList.stream.allMatch(Condition_1)){
count_Con_1.getAndIncrement();
isUnmatched = false;
list_Con_1.add(intList);
}
if(intList.stream.allMatch(Condition_2)){
count_Con_2.getAndIncrement();
isUnmatched = false;
list_Con_2.add(intList);
}
if(intList.stream.allMatch(Condition_3)){
count_Con_3.getAndIncrement();
isUnmatched = false;
list_Con_3.add(intList);
}
if(isUnmatched){
count_Unmatched.getAndIncrement();
list_Unmatched.add(intList);
}
}
// process all counts & lists
So here we are streaming 3 times for each condition ,on top of this if for a condition if I have a specific action on the elements of the intList I will again have to do a secondary intList.stream. inside the condition
Can we do all condition with one stream ? This can be done with one normal for-loop on intList , irrespective of the number of conditions with may be more variables etc.
If this is the least number of streams then how is it still better for-loop ?
How do I decide which way to go?
Edit : old-fashioned way code added with some ex conditions as asked by #cyberbrain
int count_Unmatched = 0;
int count_Con_1 = 0;
int count_Con_2 = 0;
int count_Con_3 = 0;
List<List<Integer>> list_Unmatched = new ArrayList<>();
List<List<Integer>> list_Con_1 = new ArrayList<>();
List<List<Integer>> list_Con_2 = new ArrayList<>();
List<List<Integer>> list_Con_3 = new ArrayList<>();
for (List<Integer> intList : lstOfintLst) {
boolean isUnmatched = true;
int sum = 0;
int countEven = 0;
int countOdd = 0;
int countMultipleOf3 = 0;
int numLstSize = intList.size();
for (Integer i : intList) {
//all are odd or even
if (i % 2 == 0) {
countEven++;
}
if (i % 2 == 1) {
countOdd++;
}
//all multiples of 3
if (i % 3 == 0) {
countMultipleOf3++;
}
// sum of all elements in intList is multiple of 5
sum = sum + i;
}
if (numLstSize == countEven || numLstSize == countOdd) {
count_Con_1++;
list_Con_1.add(intList);
isUnmatched = false;
}
if (numLstSize == countMultipleOf3) {
count_Con_2++;
list_Con_2.add(intList);
isUnmatched = false;
}
if (sum % 5 == 0) {
count_Con_3++;
list_Con_3.add(intList);
isUnmatched = false;
}
if (isUnmatched) {
count_Unmatched++;
list_Unmatched.add(intList);
}
}
// process all counts & lists
Well, the code you've provided looks like a bit chaotic self-imposed coding challenge or a part of a project which became unmanageable.
Always think about the use cases and data structures that would be suitable for these use cases. Don't cram everything into a single method, instead try to think how can you spit the functionalities - learn about the Single responsibility principle.
You've created four lists of lists, what's next? How are you going to work with them, how can you pass around this data - by wrapping with another list? List of lists of lists, doesn't sound nicely.
The key point is that you need to structure your data in a meaningful way.
Here's one of the possible solutions (disclaimer: I can't say for sure that it would be the most suitable approach for your project).
These lists can be store in a Map as values, and keys of the map would represent matching conditions.
We can introduce an enum encapsulating the conditional logic in a form of Predicates. Members of the enum would be used as keys.
public enum MyPredicate {
IS_EVEN("Is Even", list -> list.stream().allMatch(i -> i % 2 == 0)),
IS_ODD("Is Odd", list -> list.stream().allMatch(i -> i % 2 != 0)),
IS_DIVISIBLE_BY_THREE("Is Divisible By Three", list -> list.stream().allMatch(i -> i % 3 == 0)),
DEFAULT("No Matches", list -> true);
private static final EnumSet<MyPredicate> withoutDefault = EnumSet.range(IS_EVEN, IS_DIVISIBLE_BY_THREE);
private String predicateName;
private Predicate<List<Integer>> predicate;
MyPredicate(String predicateName, Predicate<List<Integer>> predicate) {
this.predicateName = predicateName;
this.predicate = predicate;
}
public static List<MyPredicate> getMatches(List<Integer> list) {
List<MyPredicate> matchingEnums = withoutDefault.stream()
.filter(e -> e.predicate.test(list))
.toList();
return matchingEnums.isEmpty() ? List.of(DEFAULT) : matchingEnums;
}
public boolean matches(List<Integer> list) {
return predicate.test(list);
}
#Override
public String toString() {
return predicateName;
}
}
The process of creating these resulting lists boils down to finding the matching predicate for every list in the input and mapping the list to a matching predicate.
Note: that I've ignored all the counters that you've used in code for a reason. Such logic is absolutely not viable with streams, you should not change the state functions in the stream should not use side effects and accumulate the stated outside the stream. And even with loops, these counters are redundant if you need all elements in a list to match a particular condition.
That's how it might be implemented using Stream.collect():
public static Map<MyPredicate, List<List<Integer>>> splitIntoGroups(List<List<Integer>> lists) {
return lists.stream()
.collect(
HashMap::new,
(Map<MyPredicate, List<List<Integer>>> map, List<Integer> list) ->
MyPredicate.getMatches(list).forEach(p -> {
if (p.matches(list)) map.computeIfAbsent(p, k -> new ArrayList<>()).add(list);
}),
(left, right) -> right.forEach((key, v) ->
left.computeIfAbsent(key, k -> new ArrayList<>()).addAll(v))
);
}
main()
public static void main(String[] args) {
List<List<Integer>> sourceList = List.of(
List.of(1, 2, 3), List.of(4, 6, 8), List.of(5, 7, 9), List.of(3, 9, 18)
);
splitIntoGroups(sourceList).forEach((k, v) -> System.out.println(k + " -> " + v));
}
Output:
Is Divisible By Three -> [[3, 9, 18]]
Is Even -> [[4, 6, 8]]
Is Odd -> [[5, 7, 9]]
No Matches -> [[1, 2, 3]]
I used a HashMap to store the occurrences of each element, and then iterated over the hash map to get duplicated element, but something doesn't feel right about this solution.
Problem statement in Firecode.io:
Write a method duplicate to find the repeated or duplicate elements in an array. This method should return a list of repeated integers in a string with the elements sorted in ascending order (as illustrated below).
duplicate({1,3,4,2,1}) --> "[1]"
duplicate({1,3,4,2,1,2,4}) --> "[1, 2, 4]"
Note: You may use toString() method to return the standard string representation of most data structures, and Arrays.sort() to sort your result.*
Here is my code:
public String duplicate(int[] numbers) {
HashMap < Integer, Integer > hs = new HashMap < Integer, Integer > ();
for (int i = 0; i < numbers.length; i++) {
if (hs.get(numbers[i]) == null) {
hs.put(numbers[i], 1);
} else hs.put(numbers[i], (Integer) hs.get(numbers[i]) + 1);
}
int size = 0;
for (int i: hs.keySet()) {
if (hs.get(i) > 1) {
size++;
}
}
int j = 0;
int[] a = new int[size];
for (int i: hs.keySet()) {
if (hs.get(i) > 1) {
a[j++] = i;
}
}
Arrays.sort(a);
return Arrays.toString(a);
}
Here's the way I would do it: (comments for educational purposes, would probably not have them in production code.)
public String duplicate(int[] numbers) {
// holds the items we've encountered more than once.
// TreeSet<> keeps things in sorted order for us.
final SortedSet<Integer> duplicates = new TreeSet<>();
// keeps track of items we've encountered.
final Set<Integer> encountered = new HashSet<>();
// iterate over every number
for (final int number : numbers) {
// Add the item to encountered. Set.add() will return true if
// the element is new to the set.
if (!encountered.add(number)) {
// Since the element wasn't new, ensure this item exists in the duplicates collection.
duplicates.add(number);
}
}
return duplicates.toString();
}
Since you don't have to tell how many times an element is duplicated you only need a Set to remember which elements are unique and which not. If you know the element values (e.g. numbers between 1 and 10) you could further simplify Set to boolean[] or a bit vector:
int[] numbers = {1, 3, 4, 2, 2, 1, 2, 4, 4};
Set<Integer> unique = new HashSet<>();
Set<Integer> duplicates = new HashSet<>();
for (int n : numbers) {
if (!unique.add(n)) {
duplicates.add(n);
}
}
List<Integer> result = new ArrayList<>(duplicates);
result.sort(Integer::compareTo);
System.out.println(result); // [1, 2, 4]
If you are using Java 8 or beyond you can try:
public String duplicate(int[] numbers) {
Map<Integer, Integer> hs = new HashMap<>();
for ( int i : numbers ) {
hs.merge( i, 1, Integer::sum);
}
return '[' +
hs.entrySet()
.stream()
.filter( e -> e.getValue() > 1 )
.map(Entry::getKey)
.sorted()
.map(i -> i.toString())
.collect(Collectors.joining(", ")) +
']';
}
I am almost at end of code. I am supposed to find number of times numbers appeared in a given array. The for loop is not working as expected.
They contain numbers in an array, I am able to extract individual numbers which are repeating. Then I am trying to find how many time that an individual number is appeared by using for loop and store it into another array which will give me the count.
Everything is declared like d[], b[], c[]
for (i = 0; i < k; i++) {
for (j = 0; j < l; j++) {
if (d[i] == c[j]) {
b[i]++;
}
}
}
Expected output should be, like if if's condition to be true, b[i] should increment the number of times a number appeared but it is giving me array of ones, like it is only passing through the if condition once per i.
You can do that using for example a Hashtable saving the array numbers as keys and the number of repetitions as value. This function receive an array of Integers, and returns the Hashtable with the repetitions:
public static Hashtable<Integer, Integer> getRepitedNumbers( Integer[] numbers) {
Hashtable<Integer, Integer> Result = new Hashtable<Integer, Integer>();
for( int i = 0; i < numbers.length; i++) {
if( Result.containsKey( numbers[ i])) {
//If the current number is saved in the hashtable, you need to increment the
//value (repetitions counter for this number)
Result.put( numbers[ i], Result.get( numbers[ i]) + 1);
} else {
//If the current number doesn't exist, is the first occurrence.
Result.put( numbers[ i], 1);
}
}
//At the end you have a hashtable with each number and the number of occurrences
return Result;
}
https://docs.oracle.com/javase/8/docs/api/java/util/stream/Collectors.html
public static void main(String[] args) {
List<Integer> listOfIntegers = Arrays.asList(1, 2, 3, 1, 2, 3, 3, 3, 3, 8, 9);
Map<Integer, Long> integerToCount = listOfIntegers.stream().collect(Collectors.groupingBy(it -> it, Collectors.counting()));
System.out.println(integerToCount);
}
What about using Streams:
public static Map<Integer, Long> countItems(Integer[] arr) {
return Arrays.stream(arr).collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
}
How to get max from an ArrayList that has more than one max? For example, if an ArrrayList contains max = 20 stored at index 2, 3 and 6, how do you get all that indicies?
The obvious way is to first get maximum value by Collections.max(), then collect indicies where items are equal to max:
public <T extends Comparable<? super T>> List<Integer> maxIndicies(List<T> input) {
if (input.isEmpty()) // avoid exception thrown by Collections.max() if input is empty
return Collections.emptyList();
final T max = Collections.max(input);
return IntStream.range(0, input.size())
.filter(i -> input.get(i).compareTo(max) == 0)
.boxed()
.collect(Collectors.toList());
}
Additionally, I'd like to propose another solution where iteration is performed only once. During iteration, you need to check two things for each item: 1) if it is greater than current max, set a new max and reset result list, 2) if it is equal to current max, add its index to result list:
public <T extends Comparable<? super T>> List<Integer> maxIndicies(List<T> input) {
T max = null;
List<Integer> res = new ArrayList<>();
for (int i = 0; i < input.size(); i++) {
T item = input.get(i);
if (max == null || item.compareTo(max) > 0) { // item > max => reset
res.clear();
max = item;
res.add(i);
} else if (item.compareTo(max) == 0) // item equals current max
res.add(i);
}
return res;
}
This won't give you value of max item itself, but you can get it by any returned index, simply as:
List<Integer> maxInd = maxIndicies(list);
maxValue = maxInd.isEmpty() ? null : list.get(maxInd.get(0));
This sounds like a homework for your programming course. You should do it yourself but anyway here is the solution.
private List<Integer> getAllMaxIndices(List<Integer> aList) {
List<Integer> result = new ArrayList<Integer>();
// check argument
if (aList == null || aList.isEmpty()) {
return result;
}
// initialize the list with the index of the first element
result.add(0);
Integer tmpInt;
Integer tmpFirstIndexOfMaxInt;
Integer tmpMaxInt;
for (int i = 0; i < aList.size(); i++) {
// save the current integer and the currently maximum integer
tmpInt = aList.get(i);
tmpFirstIndexOfMaxInt = result.get(0);
tmpMaxInt = aList.get(tmpFirstIndexOfMaxInt);
// if the current element is greater than the last found
if (tmpInt > tmpMaxInt) {
// empty the result
result.clear();
// start collecting indices again
result.add(i);
}
// if the current element is equal to the last found
else if (tmpInt.intValue() == tmpMaxInt.intValue()) {
// insert the current index in the result
result.add(i);
}
}
return result;
}
I will leave it to you to write the code which tests this function.
Another approach using streams. That solution assumes that you want to know how often the max occurs (not the indices).
public static Map.Entry<Integer, Long> getMaxWithOccurrences(
List<Integer> list) {
return list
.stream()
.collect(
Collectors.groupingBy(i -> i, TreeMap::new,
Collectors.counting())).lastEntry();
}
I'd use a simple and easy to read for loop.
public List<Integer> getMaxIndices(List<Integer> values) {
Integer max = Collections.max(values);
List<Integer> maxIndices = new ArrayList<>();
for (int i = 0; i < values.size(); i++) {
if (values.get(i).equals(max)) {
maxIndices.add(Integer.valueOf(i));
}
}
return maxIndices;
}
Integer maxValue = Collections.max(list);
int numberofMax = Collections.frequency(list, maxValue);
this "numberofMax" will return how many maximum values the "list" has.
usual max finders only store the maximum met value, here you will have to maintain a list of indexes matching the maximum value.
You can do it in following way:
public void findMaxIndices() {
//Your list with numbers
List<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3,4,5,6,7,8,9,1,2,3,4,5,6,7,8,9));
//Sorted Map which will contain key as numbers and value as list of indices where your 'key' exists in the list
SortedMap<Integer, List<Integer>> indexMapping = new TreeMap<Integer, List<Integer>>();
for(int i = 0; i< list.size(); i++) {
//Get the number at index i
int number = list.get(i);
//Check if any index corresponding to 'number' as index has been added to your map
List<Integer> mapping = indexMapping.get(number);
if(mapping == null) {
//instantiate the list if no index has been added yet
mapping = new ArrayList<Integer>();
//Key as your 'number'
indexMapping.put(number, mapping);
}
//Add the index of the 'number' to the mapping list, which is mapped by key as 'number'
mapping.add(i);
}
//Last key in sorted map will be your highest number in the list, get the value corresponding to it. Following prints: [8,17]
int maxNumber = indexMapping.lastKey(); //Maximum number found
System.out.println(indexMapping.get(maxNumber)); //Indices where maximum number exists
}
This way, you can also find indices with lowest values easily:
indexMapping.get(indexMapping.firstKey());