1

I'm a student new to java and I'm having difficulty with the following problem. I have a massive String array of over 1 million weather readings, each element contains a Site ID, Site Name, Site latitude, Site Longitude, Year, Month, Date(1-31), Hour, Wind speed, Temperature.

My problem is: How many days did the temperature fall to or below 0.0 anywhere in the UK. All the entries are from the same year so that isn't a problem. But the month int variable goes ranges from 1 to 12 and the date int variable ranges from 1 to 31. What would be the best way to organise the data to allow me to only count unique days?

I've already created an object class called WeatherRecords with a get method that splits the strings up by the comma and parses each element into the correct type and stores the object in an array. Like so:

        String[] weatherData = WeatherData.getData();
        ArrayList<WeatherRecords> records = new ArrayList<>();
        for (int i = 1; i < weatherData.length; i++) {
            String line = weatherData[i];
            String[] elements = line.split(",");
            String siteIdString = elements[0];
            String siteName = elements[1];
            String siteLatString = elements[2];
            String siteLonString = elements[3];
            String recordYearString = elements[4];
            String recordMonthString = elements[5];
            String recordDateString = elements[6];
            String recordHourString = elements[7];
            String recordWindSpeedString = elements[8];
            String recordTempString = elements[9];
            int siteId = Integer.parseInt(siteIdString);
            double siteLat = Double.parseDouble(siteLatString);
            double siteLon = Double.parseDouble(siteLonString);
            int recordYear = Integer.parseInt(recordYearString);
            int recordMonth = Integer.parseInt(recordMonthString);
            int recordDate = Integer.parseInt(recordDateString);
            int recordHour = Integer.parseInt(recordHourString);
            int recordWindSpeed = Integer.parseInt(recordWindSpeedString);
            double recordTemp = Double.parseDouble(recordTempString);
            WeatherRecords record = new WeatherRecords(siteId, siteName, siteLat, siteLon, recordYear, recordMonth,
                    recordDate, recordHour, recordWindSpeed, recordTemp);
            records.add(record);
        }
        return records;

    } 
Conzo
  • 13
  • 2
  • Is the input sorted on the date time columns? If so then you don't need to store them in a list. – Joakim Danielson Nov 20 '19 at 14:17
  • 1
    Would be easier if the days, months and years were encapsulated in a `LocalDate` object in an instance of `WeatherRecord` (without the trailing s because it represents a single record). – deHaar Nov 20 '19 at 14:17
  • most simple way would be to store this inside the WeatherRecords class if temperature is bellow 0.0 or equals it, then some method like getTotalBelowZeroDays and return this list size. or little bit prettier solution would be to use Comparator or comparable, sort data according the temperature and extract negative temparature count. – George Weekson Nov 20 '19 at 14:21
  • @GeorgeWeekson According to the description OP wants to count when the temp _falls_ to or below 0, so its the change from positive to zero/negative that should be counted – Joakim Danielson Nov 20 '19 at 14:25
  • @JoakimDanielson i understood and it can be easily counted while creating WeatherRecords object, because it carries recordTemp variable, it can be done with one variable inside the WeatherRecords every time temp fails to or below 0 increment variable, that's it. to evaluate and have more functionality OP can add that data to some container. – George Weekson Nov 21 '19 at 07:24
  • @JoakimDanielson after having data in container it can be easily sorted according to date or whatever is requirement, plus if data is in container incrementing some variable wont be necessary, because container (list, array) can return the size. – George Weekson Nov 21 '19 at 07:37
  • @GeorgeWeekson My main point was that if you only store records when the temp is 0 or below you don't know when and how often it reached 0 or below. – Joakim Danielson Nov 21 '19 at 08:35
  • By the way, consider using `BigDecimal` rather than `double` to avoid the inaccuracy of floating-point. – Basil Bourque Nov 21 '19 at 21:33

3 Answers3

2

Please try the code shown below. Use LocalDate to represent your year-month-day. Then use streams to filter and collect your targeted objects.

class WeatherRecord{
    double recordTemp;
    LocalDate localDate;
    String siteName;
}

…and…

WeatherRecord w1 = new WeatherRecord();
w1.setRecordTemp(1);
w1.setSiteName("UK");
w1.setLocalDate(LocalDate.of(recordYear, recordMonth, recordDate));       
records.add(w1);

List<WeatherRecord> filteredList =
    list
    .stream()
    .filter( w -> w.getRecordTemp() <= 0 )
    .collect( Collectors.toList() )
;
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
dassum
  • 4,727
  • 2
  • 25
  • 38
0

Assuming Java 8 or more:

First, change your year, month day, hour to a LocalDateTime property of your WeatherRecord (I guess the class name should drop the "s"), it will be easier to handle.

Then, I should look into stream(), where you will be ablt to filter and retrieve what you need in your arrayList of WeatherRecord.

Cromm
  • 328
  • 6
  • 25
  • Please provide a proper solution, this is more suitable as a comment. – Joakim Danielson Nov 20 '19 at 14:26
  • This helped me move in the correct direction. I altered my WeatherRecord Object to take in another variable at the end, which converted the year, month and date to a LocalDate variable using : `LocalDate localDate = LocalDate.of(recordYear, recordMonth, recordDate); ` `WeatherRecord record = new WeatherRecord(siteId, siteName, siteLat, siteLon, recordYear, recordMonth, recordDate, recordHour, recordWindSpeed, recordTemp, localDate); ` Cheers for the help guys. – Conzo Nov 20 '19 at 15:48
  • @Conzo You might run into trouble if you are going to do this for over 1 million records – Joakim Danielson Nov 21 '19 at 08:36
0

How many days did the temperature fall to or below 0.0 anywhere in the UK

The very good Answer by dassum gets us most of the way, but omitted the part about getting to a list of distinct dates. Let's push that code a bit farther.

Our WeatherSample class is shown first.

Notice that we use BigDecimal rather than float or double or Float or Double, to avoid the inaccuracy of floating-point technology.

To compare a pair of BigDecimal objects, we cannot use the operators such as < or <=. We must call compareTo of the Comparable interface, which returns a negative integer if the first object is less than the second. So we can test for the result of compareTo being less than zero. See this Question for more info.

package work.basil.example;

        import java.math.BigDecimal;
        import java.time.LocalDate;
        import java.util.Objects;

final public class WeatherSample
{
    // ----------|  Member fields  |---------------------------
    final private LocalDate localDate;
    final private int hour;
    final private String siteName;
    final private BigDecimal recordTemp;

    // ----------|  Constructors  |---------------------------
    public WeatherSample ( LocalDate localDate , int hour , String siteName , BigDecimal recordTemp )
    {
        this.localDate = Objects.requireNonNull( localDate );
        this.hour = Objects.requireNonNull( hour );
        this.siteName = Objects.requireNonNull( siteName );
        this.recordTemp = Objects.requireNonNull( recordTemp );  // Should also check for String::isBlank & String::isEmpty.
    }

    // ----------|  Accessors  |---------------------------
    public LocalDate getLocalDate ( )
    {
        return this.localDate;
    }

    public int getHour ( )
    {
        return this.hour;
    }

    public String getSiteName ( )
    {
        return this.siteName;
    }

    public BigDecimal getRecordTemp ( )
    {
        return this.recordTemp;
    }
}

Make a few instances, and collect into a non-modifiable List. We have samples for the 23rd, 24th, and 25th. Only the first and last days are hits, having temps below zero.

List < WeatherSample > samples = List.of(
        new WeatherSample( LocalDate.of( 2020 , 1 , 23 ) , 11 , "Springfield" , new BigDecimal( "-23.5" ) ) ,
        new WeatherSample( LocalDate.of( 2020 , 1 , 23 ) , 13 , "Springfield" , new BigDecimal( "10.1" ) ) ,
        new WeatherSample( LocalDate.of( 2020 , 1 , 24 ) , 11 , "Springfield" , new BigDecimal( "4.5" ) ) ,
        new WeatherSample( LocalDate.of( 2020 , 1 , 24 ) , 13 , "Springfield" , new BigDecimal( "4.7" ) ) ,
        new WeatherSample( LocalDate.of( 2020 , 1 , 25 ) , 11 , "Springfield" , new BigDecimal( "-25.0" ) ) ,
        new WeatherSample( LocalDate.of( 2020 , 1 , 25 ) , 13 , "Springfield" , new BigDecimal( "-25.7" ) )
);

Code to process that list. First we generate a stream from our list, each WeatherSample object being considered in succession. Then we filter out the ones

List < LocalDate > dates =
        samples
                .stream()
                .filter( sample -> ( sample.getRecordTemp().compareTo( BigDecimal.ZERO ) < 0 ) )
                .map( sample -> sample.getLocalDate() )
                .distinct()
                .collect( Collectors.toList() );

Dump to console.

System.out.println( "dates.toString(): " + dates );

When run.

dates.toString(): [2020-01-23, 2020-01-25]

You literally asked for just the number of days. Interrogate the List for its size.

int countOfDates = dates.size() ; 

2

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154