Find the closest object inside a list given two values - java

I have an object that follows the structure below
class Ray{
float param1;
float param2;
}
and then I have the following: List<Ray> rays
I have a 2D slider that gives a couple of values {value1, value2}
The problem is that the values inside the slider are continuous and they don't match exactly with the values of the class Ray, thus I cannot just make a filter using stream().
I need to find the Ray that has both fields closest to value1 and value2.
I'm not really expert using streams().
My first idea is to iterate the List and find the nearest value (let's say nearestParam1) to value1, then iterate the list and find the nearest value (nearestParam2) to value2, then filter the list by nearestParam1 and finally filter the list by nearestParam2.
...
List<Ray> rays;
float nearestParam1;
float value1;
float oldDistance=9999;
for (Ray ray : rays){
float newDistance = abs( ray.Param1 - value1);
if(newDistance < oldDistance) {
oldDistance = newDistance;
nearestParam1 = ray.Param1;
}
}
List<Ray> filteredRays = rays.stream().filter(ray -> ray.Param1 == nearestParam1).toList();
and then repeat again for the second field. I don't like this solution, can you find a cleaner and maybe faster solution?
My list has a really huge dimension and inside there are not equals object (1 field could match but not both at the same time)

Rather than comparing each field separately, you can consolidate them into a single value and then find the closest one to your target date using Stream#min.
public LocalDateTime toDateTime(int dayOfTheYear, double timeOfTheDay) {
LocalDate first = LocalDate.of(2022,1,1);
return first.atStartOfDay()
.plusDays(dayOfTheYear)
.plusHours((int) timeOfTheDay)
.plusMinutes((long) ((timeOfTheDay - Math.floor(timeOfTheDay)) * 60L));
}
public Optional<Ray> findClosestRay(List<Ray> rays, int DayOfTheYear, float TimeOfTheDay) {
LocalDateTime target = toDateTime(DayOfTheYear, TimeOfTheDay);
return rays.stream()
.min(Comparator.comparing(ray -> Duration.between(target, toDateTime(ray.DayOfTheYear, ray.TimeOfTheDay)).abs()));
}

as MikeFHay pointed out in the main post i didn't specified how to determine the single closest result over all rays combining minimization of param 1 and param 2.
I found a solution that is quite working for my case, what do you think about that?
Comparator<Ray> comparator = Comparator.comparing(ray->abs(ray.param1-value1) + abs(ray.param2-value2) );
Ray bestRay = rays.stream().min(comparator).get();

Related

Guava iterators for nested foreach

I want to use guava iterator or java8 foreach(may be lambda expression) nested for loop and process some statements and return a long variable. Here is my code in native java. Please excuse my code may not efficient. I read over net accessing non final variables inside new java 8 foreach is not possible.
Long x = Long.valueOf(0);
Long y = Long.valueOf(0);
for(FirstLevel first : Levels)
{
if(first.getSecondLevels() == null)
{
x= x + getSomeValue(first);
}
for (SecondLevel second : first.getSecondLevels())
{
y = y + getSomeValue(second);
}
}
return x + y;
I have tried but unable to return the values. Thanks in advance for help!
Couple things:
Before approaching "refactoring" like that one you ask, I really strongly recommend learning more "pure" Java (which I assume is the case here, #javalearner). For example you can use long literals instead of manually boxing values:
long x = 0L;
long y = 0L;
Anyway...
using Guava won't help here - this is the imperative way of doing it, and with Java 7 + Guava you'd have to write awkward anonymous classes (i.e. Functions), which without language support is painful. Which brings me to...
Java 8 and Streams. This is probably the best way to go, but first you have to fix (?) your code and define actual problem - for example this statement x= x + getSomeValue(x); evaluates x each time and does not take FirstLevel into account (same is true for y and SecondLevel), so I assume what you really meant was x =+ getSomeValue(firstLevel);.
Having all that said - please be more specific what your problem really is.
EDIT:
After your clarification, using streams your code could look like this:
final long sum = levels.stream()
.mapToLong(first -> getSomeValue(first) + first.getSecondLevels().stream().mapToLong(this::getSomeValue).sum())
.sum();
or with some helper method:
final long s = levels.stream()
.mapToLong(first -> getSomeValue(first) + getSecondLevelSum(first))
.sum();
private long getSecondLevelSum(final FirstLevel first) {
return first.getSecondLevels().stream().mapToLong(this::getSomeValue).sum();
}
First of all, there is no sense in using boxed Long values and even if you once need a boxed value, you don’t need to invoke Long.valueOf, Java already does that for you when converting a long primitive to a boxed Long object.
Further, since adding long values does not depend on the order of summands, there is no reason to maintain two variable throughout the operation, when you will add them at the end anyway:
long result=0;
for(FirstLevel first: Levels) {
result += getSomeValue(first);
for(SecondLevel second: first.getSecondLevels()) {
result += getSomeValue(second);
}
}
return result;
Note that the operator += does the same as result = result + … here, but avoids the repetition of the target operand.
Assuming that both, Levels and the result of getSecondLevels, are collections you can write the same as Stream operation as
return Levels.stream()
.mapToLong(first ->
getSomeValue(first) + first.getSecondLevels().stream()
.mapToLong(second -> getSomeValue(second)).sum())
.sum();
or, alternatively
return Levels.stream()
.flatMapToLong(first -> LongStream.concat(
LongStream.of(getSomeValue(first)),
first.getSecondLevels().stream().mapToLong(second -> getSomeValue(second))))
.sum();
If Levels is an array, you have to replace Levels.stream() with Arrays.stream(Levels) and likewise, if getSecondLevels() returns an array, you have to replace first.getSecondLevels().stream() with Arrays.stream(first.getSecondLevels())

Need to print top three values in console

I just found elements (around 100 float values) via XPath and printed them on console using System.Out.. Now I need to sort them and print top three values from them. Please help me in this regard.
Thanks
I dont know about selenium, My code is related to java. If your float number are strings like this (5.3 6.366), You can use below code:
String floatNumber = "5.3 6.366";
String[] numbers = floatNumber.split(" ");
Arrays.sort(numbers);
for(int i=0; i<numbers.length;i++){
System.out.println(numbers[i]);
}
Since yo are using SeleniumWebdriver, you probably have this as a data structure: List<WebElement>.
Lists are part of the java.util.Collection framework and therefore it is not very hard to implement a sorting. Something like this could work, if list contains the List<WebElement> and the float values that you want to sort after are in an attribute called toSortAfter:
Collections.sort(list, new Comparator<WebElement>(){
#Override
public int compare(WebElement e1, WebElement e2){
String fl1Str = e1.getAttribute("toSortAfter");
String fl2Str = e2.getAttribute("toSortAfter");
if (fl1Str != null && fl2Str != null){
float f1 = Float.parseFloat(fl1Str);
float f2 = Float.parseFloat(fl2Str);
if (f1 < f2){
return -1;
}
else if (f1 > f2) {
return 1;
}
}
return 0;
}
});
I leave better handling of Exceptions to the interested reader... Also, note that I wrote this out of my head and without a Java-Compiler at hand. So there might be syntax or other mistakes.
Note:
Without the knowledge of the HMTL nodes you are collectiong it is not possible to help you any further. It might be, that the attribute is called differently, of that the float value is not an attribute altogether. If is it the content of the Elements, you can access this with WebElement.getText() method.

JFreeChart TimeSeries array exception

this is my first question here ever, and I would appreciate if you can help me.
Since the code I have is way too large to post here, I'll try to describe what my problem is in short.
So, I have made TimeSeries array within my class and array list from where I get values for time series:
private TimeSeries[] seriesArray = new TimeSeries[10];
ArrayList<TempClass> valuesFromArrayList = new ArrayList<>();
I need to make TimeSeries array, because I want to be able to show multiple timeseries graphs. Using only one TimeSeries and addOrUpdate method isn't what I want because then values get mixed when I create more graphs. So, I add values like this:
for(int i = 0; i < valuesFromArrayList.size(); i++)
{
TempClass obj = (TempClass) valuesFromArrayList.get(i);
int timeStamp = obj.getTimeStamp();
int hrsDiff;
int minsDiff;
int secsDiff;
hrsDiff = timeStamp / 3600;
timeStamp = timeStamp - hrsDiff * 3600;
minsDiff = timeStamp / 60;
timeStamp = timeStamp - minsDiff * 60;
secsDiff = timeStamp;
seriesArray[Integer.parseInt(comboBoxValue) - 1].add(new Second(secsDiff, minsDiff, hrsDiff, day, month, year), Math.abs(obj.getValue()));
}
What this part of code does is that it reads values and timestamps from ArrayList I created. There is comboBox where user can choose which timeSeries array index will be in graph. So, if user chooses value 9 from comboBox, timeSeries from index 8 will be chosen and plotted on graph. TimeStamp is simply number of seconds that passed since 00:00:00 at day when values were taken.
TempClass is defined as:
class TempClass
{
private int timeStamp;
private double value;
public TempClass(int a, double b)
{
timeStamp = a;
value = b;
}
public int getTimeStamp()
{
return timeStamp;
}
public double getValue()
{
return value;
}
public void setValue(double val)
{
value = val;
}
}
The problem I have is that when I try to make second (2nd) graph, that is another index of TimeSeries array, I get message:
You are attempting to add an observation for the time period Thu Apr 30 00:00:00 CEST 2015 but the series already contains an observation for that time period. Duplicates are not permitted. Try using the addOrUpdate() method.
I don't want to use addOrUpdate method, I need add method. Values in ArrayList I use to put values into timeSeries are fine, I am 300% sure. I already checked input from comboBox value and it gives correct values.
I have no explanation other that for some reason, even if array index is changed, data I want to write into the series goes to the old series (that is, to the series at the old index). In other words, it seems like even if I change index of array, it keeps writing into the old array index!
It's like equivalent to this (I know this sounds crazy but that is basically what I am getting):
int[] array = new int[5];
array[0] = 1;
array[1] = 2;
System.out.println(array[0]);
And the output I get is
2
This is something I have never heard of before, and I have code similar to this I wrote here in two other places, and in that two places it goes just fine, but in this third place I keep getting that exception.
Is this some kind of bug in JVM?
Does somebody know what this could be?
I don't know too much about TimeSeries, but after skimming the docs about it it says:
"The time series will ensure that (a) all data items have the same
type of period (for example, Day) and (b) that each period appears at
most one time in the series."
Link to Docs
I'm guessing the error is pretty straight forward or a misuse of TimeSeries. It looks like you are simply adding a duplicate date and that the constraints of TimeSeries don't allow that.
You may wish to consider writing a custom class that has the functionality you want. Yet again, I don't know much about TimeSeries, but I hope this helped a little.
Your for loop will always overwrite the value with an index of 0 on seriesArray.
What I mean is, the first time it will write to [0]
The second it will write to [0] then [1]
Is this intended?
I have not looked at the docs too much, but the message says 'the series already contains an observation for that time period.' I think that loop is not doing what you want it to do.

How can I create a getter method and a setter method for a multidimensionnal array?

To begin, sorry for my English, I'm French.
Here's the problem I'm trying to solve by writing this problem in a mathematical notation:
I have couples of coordinates that look like this: (x, y)
An = {(Xn;Yn)}
array[An][(Xn;Yn)] = {{X1;Y1}{X2;Y2}...{Xz;Yz}};
In my program, I need to create a getter and setter for the multidimensionnal array.
This is my code:
//Infos for animations of objects
//Sorting
Random random1 = new Random();
int obscMin=0, obscMax=4; //I sort them to know how many obstacles will have to be created. obsc is the obstacle
int nbreObsc = obscMin + random1.nextInt(obscMax - obscMin); //nbreObsc is the number of obstacles
//End of sorting
/*Here's the model of a table:
A couple: An={(Xn;Yn)}
Tableau1[An][(Xn;Yn)]={{X1;Y1}{X2;Y2}...{Xz;Yz}};*/
float posObsc [] []=new float [nbreObsc] [2]; //New table, which will contain the positions of the obstacles
//Obstacle position getter and setter
public float[][] getPosObsc(){//getters
return posObsc;
}
public void setPosObsc(float[][] posObsc){//setters
this.posObsc=posObsc;
}
//End of obstacle position getter and setter
protected boolean detruireObsc=false; //"detruireObsc" means "destroyObstacle"
//Algorithm that defines the movement of obstacles
protected void obscDeplacemt(){
for(int i=1;i<=nbreObsc;i++){
//Sorting to determine the Xs
float ordMin=0,ordMax=width;
float ordObsc = ordMin + (float)Math.random() * (ordMax - ordMin); //ordObsc means obstacleXPosition
setPosObsc( posObsc [i][0]);
//End of sorting
}
}
//End of obstacle movement algorithm
Here's the error I get from eclipse:
The method setPosObsc(float[][]) in the type Activity01Jeux.RenderViewJoueur is not applicable for the arguments (float)
The line of code:
setPosObsc( posObsc [i][0]);
Is calling the setPosObsc() method with a single float element from the array of arrays. But the method takes an array of arrays.
To make the code compile, you could write:
setPosObsc(posObsc);
Though that may not be what you want! If you are trying to write a method that puts a float into the array at a particular point, you will need something like this:
void setObstacleAt(int obstacleIndex, int boundaryIndex, float shiftDistance) {
posObsc[obstacleIndex][boundaryIndex] = shiftDistance;
}
I'm making a wild guess at what your array contains.
As a side note, rather than writing comments to explain the method names, you might consider using longer or more precise method names without abbreviations. In Java there is no practical limit to the length of variable and method names.
By the way, well done for having the courage to write to StackOverflow in English when it's not your first language. I had no trouble understanding your question even before Runemoro's edits.
Your getter and setter methods are fine. The error is because in the last line of code in obscDeplacemt(), you are calling setPosObsc(posObscp[i][0]). posObscp[i][0] will give you a float, when your setter method needs an array of float in the parameter in order for it to work. Good luck!
Looks like what you're trying to do is:
posObsc[i][0] = ordObsc;
There's no need to use a setter if you're in the same class.
If you want to be able to externally set an element value by index (instead of overwriting the whole array), there are two ways to go about it:
1) (Not recommended)
Since you're exposing the whole array in your getter, you can theoretically call
getPostObsc()[i][0] = ordObsc;
2) (Recommended)
Change your getter and setter to
public float getPosObsc(int x, int y){
return posObsc[x][y];
}
public void setPosObsc(int x, int y, float val){
this.posObsc[x][y] = val;
}
Now you can update an element using the setter with an index:
setPostObsc(i, 0, ordObsc);

Using of getSpectrum() in Libgdx library

I know the first thing you are thinking is "look for it in the documentation", however, the documentation is not clear about it.
I use the library to get the FFT and I followed this short guide:
http://www.digiphd.com/android-java-reconstruction-fast-fourier-transform-real-signal-libgdx-fft/
The problem arises when it uses:
fft.forward(array);
fft_cpx=fft.getSpectrum();
tmpi = fft.getImaginaryPart();
tmpr = fft.getRealPart();
Both "fft_cpx", "tmpi", "tmpr" are float vectors. While "tmpi" and "tmpr" are used for calculate the magnitude, "fft_cpx" is not used anymore.
I thought that getSpectrum() was the union of getReal and getImmaginary but the values are all different.
Maybe, the results from getSpectrum are complex values, but what is their representation?
I tried without fft_cpx=fft.getSpectrum(); and it seems to work correctly, but I'd like to know if it is actually necessary and what is the difference between getSpectrum(), getReal() and getImmaginary().
The documentation is at:
http://libgdx-android.com/docs/api/com/badlogic/gdx/audio/analysis/FFT.html
public float[] getSpectrum()
Returns: the spectrum of the last FourierTransform.forward() call.
public float[] getRealPart()
Returns: the real part of the last FourierTransform.forward() call.
public float[] getImaginaryPart()
Returns: the imaginary part of the last FourierTransform.forward()
call.
Thanks!
getSpectrum() returns absolute values of complex numbers.
It is calculated like this
for (int i = 0; i < spectrum.length; i++) {
spectrum[i] = (float)Math.sqrt(real[i] * real[i] + imag[i] * imag[i]);
}

Categories

Resources