java collec to list ignored - java

I am trying to write lambda in java that filter list by month and add data in the current month to the new list but when I try to collect the data I get an error collect is ignored.
public String getMonthlyExpensesNew() {
Functions functions = new Functions();
List<ShoppingMgnt> monthlyData = new ArrayList<>();
try {
monthlyData = getRecordsAsList();
monthlyData.stream().filter(date -> functions.checkForCurrentMonth(date.getPurchaseDate())).collect(Collectors.toList());
}catch (SQLException sqlException){
System.err.println("Error in getMonthlyExpensesNew");
}
return String.valueOf(monthlyData);
}
public boolean checkForCurrentMonth(String givenDate){
LocalDate currentDate = LocalDate.now();
LocalDate monthToCheck = LocalDate.parse(givenDate);
return currentDate.getMonth().equals(monthToCheck.getMonth());
}

your final function will be as follow:
public String getMonthlyExpensesNew() {
Functions functions = new Functions();
List<ShoppingMgnt> monthlyData = new ArrayList<>();
try {
monthlyData = getRecordsAsList();
//put the returned list in the same defined list
monthlyData = monthlyData.stream()
.filter(date -> functions.checkForCurrentMonth(date.getPurchaseDate()))
.collect(Collectors.toList());
}catch (SQLException sqlException){
System.err.println("Error in getMonthlyExpensesNew");
}
//the return with updated list
return String.valueOf(monthlyData);
}

Your initial code:
monthlyData.stream()
.filter(date -> functions.checkForCurrentMonth(date.getPurchaseDate()))
.collect(Collectors.toList());
Within this line the collect operation returns a List. You should store this List into your monthlyData reference to be returned later. So you should write like this:
monthlyData = monthlyData.stream()
.filter(date -> functions.checkForCurrentMonth(date.getPurchaseDate()))
.collect(Collectors.toList());

Related

How to replace the for each using streams in java

Dates.forEach(date -> {
executeQuery(date, loads);
private void executeQuery(LocalDate date, ArrayList<Load> loads){
MapSqlParameterSource source = new MapSqlParameterSource();
source.addValue("date", date.toString());
Load load = namedJdbcTemplate.queryForObject(Constants.SQL_QUERY, source,
new BeanPropertyRowMapper<>(Load.class));
loads.add(load);
}
How can I use the streams concept for the above code
Something like this should work
// change your method like so
private Load executeQuery(LocalDate date){
MapSqlParameterSource source = new MapSqlParameterSource();
source.addValue("date", date.toString());
return namedJdbcTemplate.queryForObject(Constants.SQL_QUERY, source,
new BeanPropertyRowMapper<>(Load.class));
}
// load your dates from somewhere
List<LocalDate> dates = getYourDates();
// now use the streams API to collect the query results into a new list
List<Load> loads = dates.stream()
.map(this::executeQuery)
.collect(Collectors.toList());
or
List<Load> loads = getYourDates().stream()
.map(this::executeQuery)
.collect(Collectors.toList());

How can i convert it to java stream

I am pretty new to java8 streams. I was trying to work on collection of objects using stream. But not able to achieve in precise way.
Below is the snippet which I achieved (which is giving wrong result). expected end result is List<String> of "Names email#test.com".
recordObjects is collection of object
choices = recordObjects.stream()
.filter(record -> record.getAttribute
(OneRecord.AT_RECORD_SUBMITTER_TABLE_EMAIL) != null)
.filter(record -> !record.getAttributeAsString
(OneRecord.AT_RECORD_SUBMITTER_TABLE_EMAIL).isEmpty())
.map(record -> record.getMultiValuedAttribute
(OneRecord.AT_RECORD_SUBMITTER_TABLE_EMAIL, String.class))
.flatMap(Collection::stream)
.map(email -> getFormattedEmailAddress(ATTRI_AND_RECORD_CONTACT_DEFAULT_NAME, email))
.collect(Collectors.toList());
but below is the exact logic i want to implement using streams.
for (CallerObject record : recordObjects) {
List<String> emails = record.getMultiValuedAttribute(
OneRecord.AT_RECORD_SUBMITTER_TABLE_EMAIL, String.class);
List<String> names = record.getMultiValuedAttribute(
OneRecord.AT_RECORD_SUBMITTER_TABLE_NAME, String.class);
int N = emails.size();
for (int i = 0 ; i < N ; i++) {
if(!isNullOrEmpty(emails.get(i)))
{
choices.add(getFormattedEmailAddress(isNullOrEmpty(names.get(i)) ?
ATTRI_AND_RECORD_CONTACT_DEFAULT_NAME : names.get(i) , emails.get(i)));
}
}
}
Since we don't know the getFormattedEmailAddress method, I used String.format instead to achieve the desired representation "Names email#test.com":
// the mapper function: using String.format
Function<RecordObject, String> toEmailString = r -> {
String email = record.getMultiValuedAttribute(OneRecord.AT_RECORD_SUBMITTER_TABLE_EMAIL, String.class);
String name = record.getMultiValuedAttribute(OneRecord.AT_RECORD_SUBMITTER_TABLE_NAME, String.class);
if (email != null) {
return String.format("%s %s", name, email);
} else {
return null;
}
};
choices = recordObjects.stream()
.map(toEmailString) // map to email-format or null
.filter(Objects::nonNull) // exclude null strings where no email was found
.collect(Collectors.toList());
Changed your older version code to Java 8
final Function<RecordedObject, List<String>> filteredEmail = ro -> {
final List<String> emails = ro.getMultiValuedAttribute(
OneRecord.AT_RECORD_SUBMITTER_TABLE_EMAIL, String.class);
final List<String> names = ro.getMultiValuedAttribute(
OneRecord.AT_RECORD_SUBMITTER_TABLE_NAME, String.class);
return IntStream.range(0, emails.size())
.filter(index -> !isNullOrEmpty(emails.get(index)))
.map(index -> getFormattedEmailAddress(isNullOrEmpty(names.get(index)) ?
ATTRI_AND_RECORD_CONTACT_DEFAULT_NAME : names.get(index) , emails.get(index)))
.collect(Collectors.toList());
};
recordObjects
.stream()
.map(filteredEmail)
.flatMap(Collection::stream)
.collect(Collectors.toList());

Use parallel stream in array list with original order

I have a product, I wanna populate products in another array with the same original order, I used parallel Stream and the result was not ordered with the original order
List<Product> products = productList.getProducts();
List<ProductModelDTOV2> productModelDTOV2s = new ArrayList<>();
products.parallelStream().forEach(p -> {
try {
ProductModelDTOV2 ProductModelDTOV2 = dtoFactoryV2.populate(p, summary);
productModelDTOV2s.add(ProductModelDTOV2);
} catch (GenericException e) {
log.debug(String.format("Unable to populate Product %s", p));
}
});
return productModelDTOV2s;
It seems like this part of the code can be unordered and be run in parallel:
ProductModelDTOV2 ProductModelDTOV2 = dtoFactoryV2.populate(p, summary);
But this part must be ordered:
productModelDTOV2s.add(ProductModelDTOV2);
What you can do is to separate those two things. Do the first part in a flatMap, and the second part in forEachOrdered:
products.parallelStream().flatMap(o -> { // this block will be done in parallel
try {
return Stream.of(dtoFactoryV2.populate(p, summary));
} catch (GenericException e) {
// don't expect this message to be printed in order
log.debug(String.format("Unable to populate Product %s", p));
return Stream.of();
}
})
.forEachOrdered(productModelDTOV2s::add); // this will be done in order, non-parallel
The correct way to do this, would be to have the Stream create the list:
List<Product> products = productList.getProducts();
return products.parallelStream()
.map(p -> {
try {
return dtoFactoryV2.populate(p, summary);
} catch (GenericException e) {
log.debug("Unable to populate Product " + p);
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());

JAVA8: Find the file with the newest date in its filename

I have few files in an FTP folder with names like:
mainimport_31052017.csv
mainimport_21052017.csv
mainimport_15052017.csv
And I have a pattern string:
String pattern = "mainimport_ddmmyyy";
Now I am supposed to download the file with the latest date in its title. I am supposed to do it with Java 8 goodies.
I have a solution, but this is not pretty enough, I am doing it in 2 statements:
1) I first get the newest date:
Date newestDate = Collections.max(ftpFiles.stream().filter(fileName -> StringUtils.startsWith(fileName.getName(), prefix)).map(fileName -> {
String fileNameSuffix = fileName.getName().split("_")[1];
Date date = null;
try {
date = dateFormat.parse(fileNameSuffix);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}).collect(Collectors.toList()));
2) I then get the filename with the latest date:
Optional<FTPFile> file = ftpFiles.stream().filter(fileName->{
String fileNameSuffix = fileName.getName().split("_")[1];
Date date = null;
try {
date = dateFormat.parse(fileNameSuffix);
} catch (ParseException e) {
e.printStackTrace();
}
return StringUtils.startsWith(fileName.getName(), prefix) && date.equals(newestDate);
}).findFirst();
I am trying to do this both in a single statement, if it is possible.
Assuming that the dates always have the specified six-character representation, you may use
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file -> file.getName()
.replaceFirst(".*_([0-9]{2})([0-9]{2})([0-9]{4}).*", "$3$2$1")));
The factory method Comparator.comparing allows you to create a Comparator based on a property, so that the maximum element will be the element with the maximum value for that property.
Note that this simply converts the ddmmyyyy formatted date to a yyyymmdd string which can be compared lexicographically, which works as long as the day and months always have a two-digit form, i.e. with a leading zero.
You may optimize this a bit by preparing and reusing the regex pattern:
Pattern pattern = Pattern.compile(".*_([0-9]{2})([0-9]{2})([0-9]{4}).*");
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file ->
pattern.matcher(file.getName()).replaceFirst("$3$2$1")));
If the DateFormat is an unavoidable prerequisite, you may use
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file -> {
String name = file.getName();
name = name.substring(name.indexOf('_')+1);
try {
return dateFormat.parse(name);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}));
This performs the operation in one go, but has the disadvantage of performing the parsing operation more than necessary during the comparisons. If you want to avoid that, you may resort to the original two pass design, but you still don’t need to collect into a List:
ftpFiles.stream()
.map(FTPFile::getName)
.filter(name -> name.startsWith(prefix))
.map(name -> {
name = name.substring(name.indexOf('_')+1);
try {
return dateFormat.parse(name);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
})
.max(Comparator.naturalOrder())
.map(date -> prefix+'_'+dateFormat.format(date))
.flatMap(fileName -> ftpFiles.stream()
.filter(file -> file.getName().equals(fileName)).findAny())
.ifPresent(System.out::println);

How to sort String[] in List by timestamp?

List<String[]> data = Importer.readCsv("test.csv");
A String[] item looks like this:
Position: name (String)
Position: 2017-03-14 18:22:44.149 (String)
What is a good approach in order to sort the list by the timestamp descending? I search for a solution without creating new Objects.
Use Collection.sort() and Comparator.comparing(), I find those easy to read when coming back to it later:
data.sort(Comparator.comparing((String[] o) -> o[1]).reversed());
You can compare the timestamp without conversion, as the format is "correctly" ordering the fields.
If you are confined to Java 7, there will be more boilerplate:
Collections.sort(data, new Comparator<String[]>()
{
#Override
public int compare(String[] o1, String[] o2)
{
return o2[1].compareTo(o1[1]);
// return -o1[1].compareTo(o2[1]);
}
});
Note that I need to compare o2 to o1 in this order or negate the result of comparing o1 to o2 to order the data descending. This becomes much more obvious in the first approach.
Well, thank you very much for your suggestions and impulses.
Special Thanks goes to XtremeBaumer!
Following code does the job too:
List<String[]> data = importer.readCsv(context, csvFile);
Collections.sort(data, new Comparator<String[]>() {
#Override
public int compare(String[] item1, String[] item2) {
String dateString1 = item1[1];
String dateString2 = item2[1];
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
Date date1 = null;
Date date2 = null;
try {
date1 = format.parse(dateString1);
date2 = format.parse(dateString2);
} catch (ParseException e) {
e.printStackTrace();
return 0;
}
// dateString1 is an earlier date than dateString2
return date1.compareTo(date2);
}
});
You could use a custom Comparator that takes the second element in each array and converts it to a Timestamp. Note that a Comparator's function can't throw an exception, so you'd have to catch the potential ParseException and deal with it:
Function<String[], Date> parser = s -> {
try {
return new SimpleDateFormat("yyyy-MM-dd hh:mm:ss.SSS").parse(s[1]);
} catch (ParseException e) {
return null;
}
};
list.sort(Comparator.comparing(parser));

Categories