Reputation: 97
I want to get the index of min, I tried ways like getIndexOf etc. but none of them worked. How can I do this?
import java.util.Arrays;
class getIndexOfMin {
public static void main(String[] args) {
double arr[] = {263.5, 393.75, 5.0, 289.75};
double min = Arrays.stream(arr).min().getAsDouble();
System.out.println(min);
}
}
Upvotes: 5
Views: 711
Reputation: 19108
I think the following solution should be preferred, as in 1 iteration of the stream you collect both the required index and the required value.
double arr[] = {263.5, 393.75, 5.0, 289.75};
Optional<Map.Entry<Integer, Double>> keyValueOpt = IntStream.range(0, arr.length)
.mapToObj(i -> Map.entry(i,arr[i]))
.min(Map.Entry.comparingByValue());
keyValueOpt.ifPresent(keyValue -> System.out.println(String.format("Index %s with min value %s", keyValue.getKey(), keyValue.getValue())));
You also don't need any new classes to be introduced to your code as the Map.Entry(K k, V v)
fits the role of keeping a single pair of Key, Value to your result.
Upvotes: 0
Reputation: 29028
Here's how it can be done without iterating over the array indices and trying to mimic for
-loop with a stream.
And we can also grab the actual minimal value along the way.
For that we can use DoubleStream.collect()
which expects three arguments:
Supplier<R>
supplier - provides a mutable object which would serve as a container of data;ObjDoubleConsumer<R>
accumulator - determines how stream elements should be accumulated in the mutable collector provided by the supplier;BiConsumer<R,R>
combiner - combines partial results while executing the stream in parallel.As a mutable container which should be provided by the supplier we can use an array double[]
(if you have a look at the source code of Collectors that are intended to accumulate primitive values, namely summingInt()
, summingDouble()
, etc. you would probably find some similarities).
double[] arr = {263.5, 393.75, 5.0, 289.75};
double[] min = Arrays.stream(arr)
.collect(
() -> new double[]{0, -1, 0}, // supplier
(double[] res, double next) -> { // accumulator
res[2]++; // element count
if (res[1] == -1 || res[0] > next) {
res[0] = next; // min value
res[1] = res[2] - 1; // corresponding index (element count - 1)
}
},
(left, right) -> { // combiner
if (left[0] > right[0]) {
left[0] = right[0];
left[1] = left[2] + right[1];
}
left[2] += right[2];
}
);
System.out.printf("Min value is %s at index: %d", min[0], (int) min[1]);
Output:
Min value is 5.0 at index: 2
The logic for determining the min value and tracking the number of consumed elements shown above can be encapsulated into a class
(as suggested by @Holger) which would be used as the accumulation type instread of array.
For convenience, I've implemented DoubleConsumer
interface, its method accept()
would be used to implement accumulator. And method merge()
would be used in the combiner.
public static class MinValueAndIndex implements DoubleConsumer {
private int totalCount;
private int index;
private double min;
@Override
public void accept(double value) {
if (totalCount == 0 || value < min) {
min = value;
index = totalCount;
}
totalCount++;
}
public void merge(MinValueAndIndex other) {
if (min > other.min) {
min = other.min;
index = totalCount + other.index;
}
totalCount += other.totalCount;
}
// getters
}
The stream would look like that:
double[] arr = {263.5, 393.75, 5.0, 289.75};
MinValueAndIndex valueIndex = Arrays.stream(arr)
.collect(
MinValueAndIndex::new,
MinValueAndIndex::accept,
MinValueAndIndex::merge
);
System.out.printf("Min value is %s at index: %d", valueIndex.getMin(), valueIndex.getIndex());
Output:
Min value is 5.0 at index: 2
Upvotes: 1
Reputation: 6985
You can use a custom class to hold together an index and the value at this index. The task to find the index with min value becomes trivial then, and you can find the index and value all at once.
A record is perfect for this purpose, if you use version before java 14, you can just use normal class.
public record IndexValue(int index, double value) {
}
Example usage:
public class Test {
public static void main(String[] args) {
double[] arr = {263.5, 393.75, 5.0, 289.75};
IndexValue min = IntStream.range(0, arr.length) //stream indexes
.mapToObj(i -> new IndexValue(i, arr[i])) //map index and value together using the class
.min(Comparator.comparing(IndexValue::value)) //find minimum by comparing values
.orElseGet(() -> new IndexValue(-1, Double.MAX_VALUE)); //supply default value suiting your needs, if array was empty
System.out.println("Min index value - " + min);
}
}
Prints - Min index value - IndexValue[index=2, value=5.0]
. All operations in this stream are non-interfering and stateless, and it's safe for parallel execution.
Upvotes: 0
Reputation: 272685
You can make a stream of the indices of the array instead, from 0 to arr.length
(exclusive):
Optional<Integer> minIndex = IntStream.range(0, arr.length).boxed()
.min(Comparator.comparingDouble(i -> arr[i]));
Note that this is an optional, as the array could be empty. Also, when there are multiple minimums, it is not specified which one's index it will return. In the implementation that I'm using (OpenJDK 17), it returns the first one's index.
If you want to control which minimum's index you get, you can do:
Optional<Integer> minIndex = IntStream.range(0, arr.length).boxed().reduce(
// gets the first one's:
(a, b) -> arr[a] <= arr[b] ? a : b
// gets the last one's:
// (a, b) -> arr[a] < arr[b] ? a : b
);
Upvotes: 4
Reputation: 165
int index;
for (int i = 0; i < arr.length; i++){
double d = arr[i];
if (d == min){
index = i;
break;
}
}
Upvotes: 2