Stream query with two lists - java

I have a list of Visit objects, now I want to build another list containing available hours for given day;
public class Visit {
private int id;
private Date date;
private Time time;
private Pet pet;
private Vet vet;
this is the array String[] containing all visit hours:
public class VisitTime {
private static final String[] visitTime =
{"09:00:00","09:30:00","10:00:00","10:30:00","11:00:00","11:30:00","12:00:00",
"12:30:00","13:00:00","13:30:00","14:00:00","14:30:00","15:00:00","15:30:00","16:00:00","16:30:00"};
so now Im getting from Db list of visits (each visit has defined time), and checking if there is any other free time to schedule a visit.
I have written two methods to do so, one with iteration second with streams, both working as expected.
What I'm asking is how can I rebuild this method to NOT use terminal method twice.
public List<String> getHoursAvailable12(int vetId, String date){
List<Visit> visitList = getVisitByVetIdAndDate(vetId, date);
List<String> hoursAvailable = new ArrayList<>(Arrays.asList(VisitTime.getVisittime()));
List<String> hoursTaken = visitList.stream().map(Visit::getTime).map(Time::toString).collect(Collectors.toList());
return hoursAvailable.stream().filter(x -> !hoursTaken.contains(x)).collect(Collectors.toList());
}
and here is old-school method with collections:
public List<String> getHoursAvailable(int vetId, String date){
List<Visit> visitList = getVisitByVetIdAndDate(vetId,date);
ArrayList<String> hoursAvailable = new ArrayList<>(Arrays.asList(VisitTime.getVisittime()));
for(Visit x : visitList){
{
String time = x.getTime().toString();
if(hoursAvailable.contains(time)) hoursAvailable.remove(time);
}
}
return hoursAvailable;
}

You can try this. You get some benefit here, contains is faster in HashSet compared to List
public Set<String> getHoursAvailable(int vetId, String date){
List<Visit> visitList = getVisitByVetIdAndDate(vetId,date);
Set<String> hoursAvailable = new LinkedHashSet<>(
Arrays.asList(VisitTime.getVisittime()));
visitList.stream()
.map(Visit::getTime)
.map(Time::toString)
.forEach(vt-> hoursAvailable.removeIf(s->s.equals(vt)));
return hoursAvailable;
}

Related

Sort a list by another list

I have a little problem that is driving me crazy.
I have a
List<Integer> with ids.
List<ObjectA> with 3 variables:
an id, and two string
I have to sort the second list by putting at the top the elements with id contained in the first list, then by string asc and by the second string asc.
What is the easiest way to make this work? I am trying to use the .sort(), Comparators etc.
An example:
#Getter
#Setter
public class ObjectA {
private Integer id;
private String code;
private String name;
}
// comparator:
static class SortByCode implements Comparator<ObjectA> {
public int compare(ObjectA a, ObjectA b) {
String as = a.getCode();
String bs = b.getCode();
return as.compareTo(bs);
}
}
static class SortByName implements Comparator<ObjectA> {
public int compare(ObjectA a, ObjectA b) {
String as = a.getName();
String bs = b.getName();
return as.compareTo(bs);
}
}
// then in service:
List<Integer> idsPreferred = new ArrayList<>();
List<ObjectA> listObj = new ArrayList<>();
idsPreferred = .... add preferred ids;
listObj = .... add objects;
listObj.sort(new SortByCode()).thenComparing(new SortByName());
With this i sort by code and by name - but i need to add the sorting by the first list - I need the elements that have an id contained in the List to come before the others.
I suppose something like this using chained comparing by extracted key:
listObj.sort(Comparator.comparing(o -> !idsPreferred.contains(((ObjectA) o).getId()))
.thenComparing(o -> ((ObjectA) o).getId())
.thenComparing(o -> ((ObjectA) o).getCode())
.thenComparing(o -> ((ObjectA) o).getName()));
or
listObj.sort(Comparator.comparing(ObjectA::getId,
(id1,id2)-> {if (!((idsPreferred.contains(id1))^idsPreferred.contains(id2)))
return 0;
else return (idsPreferred.contains(id2))?1:-1;})
.thenComparing(ObjectA::getId)
.thenComparing(ObjectA::getCode)
.thenComparing(ObjectA::getName));
The solution will involve 2 steps-
check id of objects from second list, which are present in first list.
Sort the contained objects using either of the solutions suggested- How to sort List of objects by some property

Java - Retrive Indivudual Values from a List in a Map

I have a TreeMap with 3 entries, all from individual ArrayLists.
I use the following code:
Map<String, List<String>> mapOne = new TreeMap<String, List<String>>();
List<String> listFour = Arrays.asList("");
ArrayList<String> listOne = new ArrayList<>();
listOne.add("Writer");
listOne.add("Actor");
listOne.add("Politician");
listOne.add("Dancer");
ArrayList<String> listTwo = new ArrayList<>();
listTwo.add("James");
listTwo.add("Robert");
listTwo.add("Tereza");
listTwo.add("John");
ArrayList<String> listThree = new ArrayList<>();
listThree.add("Joyce");
listThree.add("Redford");
listThree.add("May");
listThree.add("Travolta");
for (int i = 0; i < listOne.size(); i++) {
String stringOne = listOne.get(i);
String stringTwo = listTwo.get(i);
String stringThree = listThree.get(i);
listFour = Arrays.asList(stringTwo, stringThree);
mapOne.put(stringOne, listFour);
}
Now I want to obtain the individual String values from the sorted list. like so:
for (Map.Entry<String, List<String>> entry : mapOne.entrySet()) {
String key = entry.getKey();
for (String value : entry.getValue()) {
System.out.println(value);
}
}
The above code prints a list like
{Robert Redford , John Travolta , Tereza May , James Joyce}
Is it possible to iterate over the list in a way as to obtain to separate lists, one with the first names and the other with the last names?
listOne = {Robert , John , Tereza, James}
listTwo = {Redford, Travolta, May, Joyce}
Or should I use an entirely different approach?
The whole thing started out with the need to sort one ArrayList and to other accordingly. What seemed trivial at the beginning, turned out to be a real challenge.
I am a sort of a hobby programmer, so the pros out there kindly bear with me.
It seems you're over-engineering somehow.
I will answer with a Stream solution first, just for the sake of trying it.
Note that I'd, personally, prefer the "old" iterative approach (see below).
// You can see by the use of AtomicInteger that this isn't the right road to take!
final AtomicInteger i = new AtomicInteger();
final Collection<List<String>> values1 =
mapOne.values()
.stream()
.flatMap(v -> v.stream())
.collect(partitioningBy(o -> i.getAndIncrement() % 2 != 0))
.values();
Output: [[Robert, John, Tereza, James], [Redford, Travolta, May, Joyce]]
Iterative approach
final Collection<String> names = new ArrayList<>();
final Collection<String> surnames = new ArrayList<>();
for (final List<String> value : mapOne.values()) {
names.add(value.get(0));
surnames.add(value.get(1));
}
Output:
[Robert, John, Tereza, James]
[Redford, Travolta, May, Joyce]
This is safe because you know each inner List has two elements.
What JB Nizet is telling you to do (credit to him for writing that), is basically to create an appropriate class
public class Person {
private String profession;
private String name;
private String surname;
// Getters and setters. JavaBean style
}
And proceed to sort a Collection<Person>.
For example, keeping it as simple as possible
final List<Person> persons = new ArrayList<>();
// Populate list
Collections.sort(persons, (p1, p2) -> {
// Not null-safe
return p1.getName().compareTo(p2.getName());
});
This will sort the list by name. It will not return a new List, but simply modify the input one.
Putting all the valuable tips, online searches and my efforts together, this is my final solution (in the hope that it might be useful to others). It seems to me my solution is minimal and straightforward :
List<Sites> listOfSites = new ArrayList<Sites>();
for (int i = 0; i < classLists.listSites.size(); i++) {
String site = classLists.listSites.get(i);
String description = classLists.listSitesDesc.get(i);
String link = classLists.listSitesLinks.get(i);
listOfSites.add(new Sites(site, description, link));
}
Collections.sort(listOfSites, (p1, p2) -> {
return p1.getName().compareTo(p2.getName());
});
final ArrayList<String> titles = new ArrayList<>();
final ArrayList<String> descriptions = new ArrayList<>();
final ArrayList<String> links = new ArrayList<>();
for (Sites s : listOfSites) {
String name = s.getName();
String description = s.getDesc();
String link = s.getLink();
titles.add(name);
descriptions.add(description);
links.add(link);
}
Below the class Sites:
public class Sites implements Comparable<Sites> {
private String name;
private String description;
private String link;
public Sites(String name, String description, String link) {
this.name = name;
this.description = description;
this.link = link;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return description;
}
public void setDesc(String description) {
this.description = description;
}
public String getLink() {
return link;
}
public void setLink(String link) {
this.link = link;
}
}

Looping within multiple arrays in Java Stream

I would need some help optimising the way to iterate through a list of 200 lines.
My 1st class is as below
Class Vehicle
Date date
int nbOfVehicleArriving
Class Planning
int date
int nbOfallowedVehicles
So, I have a list of Vehicles arriving per time ex:
01/01/2018 00:00:00 12
01/01/2018 00:10:00 10
01/01/2018 01:00:00 5
01/01/2018 01:10:00 10
....
And I have some agenda
01/01/2018 00:00:00 3
01/01/2018 00:10:00 2
01/01/2018 01:00:00 3
01/01/2018 01:10:00 5
I need to calculate the number of vehicles in the queue.
What i'm planning to do is to iterate with the Vehicles and then in it iterate with the Planning.
Is there a better way of doing this??
Thanks in advance
You could also use flatMap to merge the two arrays together and then simply extract the counts (this would involve casting) and sum these up.
Here is a full working example demonstrating how you can use flatMap:
import java.util.Arrays;
import java.util.Date;
import java.util.stream.Stream;
public class MergeStreams {
public static void main(String[] args) {
Vehicle[] vehicles = {new Vehicle(new Date(), 10), new Vehicle(new Date(), 11)};
Planning[] plannings = {new Planning(new Date(), 5), new Planning(new Date(), 12)};
int sum = Stream.of(vehicles, plannings)
.flatMap(Arrays::stream)
.map(object -> object instanceof Vehicle ? ((Vehicle) object).getNbOfVehicleArriving() : ((Planning) object).getNbOfallowedVehicles())
.mapToInt(Integer::intValue)
.sum();
System.out.println(sum);
}
}
class Vehicle {
private Date date;
private int nbOfVehicleArriving;
public Vehicle(Date date, int nbOfVehicleArriving) {
this.date = date;
this.nbOfVehicleArriving = nbOfVehicleArriving;
}
public int getNbOfVehicleArriving() {
return nbOfVehicleArriving;
}
}
class Planning {
private Date date;
private int nbOfallowedVehicles;
public Planning(Date date, int nbOfallowedVehicles) {
this.date = date;
this.nbOfallowedVehicles = nbOfallowedVehicles;
}
public int getNbOfallowedVehicles() {
return nbOfallowedVehicles;
}
}
If you run this example it will output 38 on the console.
For 200 lines it probably doesn't make any difference what solution you choose. But if you want a solution which scales to a very long list the correct approach is to zip the two lists (or streams) together (this way you avoid looping twice), and use a function to produce the combined result. Guava itself provides a zip() method, but you can also write your own.
Not sure what you mean by 'number of vehicles in the queue', but I am going to assume that you want to know how many were left in the end that have not been allowed in yet.
You can create your own class which maps the vehicles arriving with the agenda for that date.
public class VehiclePlanningData {
private final Vehicle vehicle;
private final Planning planning;
public VehiclePlanningData(Vehicle vehicle, Planning planning) {
this.vehicle = vehicle;
this.planning = planning;
}
public Vehicle getVehicle() {
return vehicle;
}
public Planning getPlanning() {
return planning;
}
}
Once you have this you can easily do a reduce() operation to carry forward any vehicles left over from the previous slot.
So something like this (using the Guava Streams class):
int queue = Streams.zip(vehicles.stream(), planning.stream(), (v, p) -> new VehiclePlanningData(v, p))
.reduce(0, (queue, nextSlot) ->
queue + (nextSlot.getVehicle().getNbOfVehicleArriving()
- nextSlot.getPlanning().getNbOfallowedVehicles(),
(q1, q2) -> q1 + q2);
UPDATE:
Seems that the queue is per time slot. In that case you might want to have a special class like this, which stores the queue size per slot:
public class TimeslotQueue {
private final Date date;
private final int queueSize;
public VehicleQueue(Date date, int queueSize) {
this.date = date;
this.queueSize = queueSize;
}
public Date getDate() {
return date;
}
public int getQueueSize() {
return queueSize;
}
}
Then get a stream of these classes like this:
List<TimeslotQueue> queues = Streams.zip(vehicles.stream(), planning.stream(),
(v, p) -> new TimeslotQueue(v.getDate(),
v.getNbOfVehicleArriving() - p.getNbOfallowedVehicles()))
.collect(Collectors.toList());
This way you have a list of queued vehicles for each date. As a side note, I would use the Java 8 LocalDateTime not the old Date class.
You may try this out.
Map<Date, Integer> agendasByDate = agendas.stream()
.collect(Collectors.toMap(Planning::getDate, Planning::getNbOfallowedVehicles));
Map<Date, Integer> vehiclesInQueueByDate = vehicles.stream().collect(Collectors.toMap(Vehicle::getDate,
v -> v.getNbOfVehicleArriving() - agendasByDate.get(v.getDate())));

Return String continuously - Data getting overwritten by ArrayList

Currently creating a tableview using JavaFX and came accross this problem where it would simply append the last element of the array (As all the other elements get overrwritten ..)
public void companyTable() {
for(CompanyData s: companydataList()){
companyDataTableView.getItems().setAll(s);
}
}
Where companyDataList is:
private List<CompanyData> companydataList(){
CompanyData company = new CompanyData("test",9,1);
for(String i : sim.getCompanyNames()) {
company.setPFCompanyName(i);
}
for(int j : sim.getCompanyValues()) {
company.setPFShareValues(j);
}
List<CompanyData> companydata = new ArrayList<>();
companydata.add(company);
return companydata;
}
The data gets added to this (Setters and getters of Strings)
private final StringProperty PFCompanyName;
private final IntegerProperty PFShareValues;
public CompanyData(String CompanyName, int ShareValue, int ClosingPence) {
this.PFCompanyName = new SimpleStringProperty(CompanyName);
this.PFShareValues = new SimpleIntegerProperty(ShareValue);
}
public String getPFCompanyName() {
return PFCompanyName.get();
}
public StringProperty PFCompanyNameProperty() {
return PFCompanyName;
}
public void setPFCompanyName(String PFCompanyName) {
this.PFCompanyName.set(PFCompanyName);
}
public int getPFShareValues(int j) {
return PFShareValues.get();
}
public IntegerProperty PFShareValuesProperty() {
return PFShareValues;
}
public void setPFShareValues(int PFShareValues) {
this.PFShareValues.set(PFShareValues);
}
Currently the output is:
CompanyName CompanyValue
Samsung 1093
But what I desire is:
CompanyName CompanyValue
Nokia 3
Apple 1
HTC 9
Samsung 1093
The method setAll(...) replaces all the elements currently in the list with the ones you provide (it "sets them all"). So each time you iterate through your loop, you replace all the elements with the current one. At the end you will just have one element in the table.
An ObservableList is a subtype of the standard java.util.List, so you can call any of the standard list methods. E.g. you can just add each element instead:
public void companyTable() {
for(CompanyData s: companydataList()){
companyDataTableView.getItems().add(s);
}
}
Of course, you don't really need to write the loop yourself, you can just add them all:
public void companyTable() {
companyDataTableView.getItems().addAll(companydataList());
}
or, if it's what you need, set them all:
public void companyTable() {
companyDataTableView.getItems().setAll(companydataList());
}
Furthermore, your companydataList() method only creates one CompanyData instance, and then constantly changes it. Here is your current implementation, with comments explaining what each line you wrote does:
private List<CompanyData> companydataList(){
// create a single instance:
CompanyData company = new CompanyData("test",9,1);
// repeatedly change the name of that instance:
for(String i : sim.getCompanyNames()) {
company.setPFCompanyName(i);
}
// repeatedly change the value of that instance:
for(int j : sim.getCompanyValues()) {
company.setPFShareValues(j);
}
// create an empty list:
List<CompanyData> companydata = new ArrayList<>();
// add one object to the list
companydata.add(company);
// return the list containing the single object:
return companydata;
}
You need to create a CompanyData instance for each of the name/value pairs, and add each instance to the list. Assuming sim.getCompanyNames() and sim.getCompanyValues() return lists (or arrays; I will assume they are lists) of the same length, you need to do something like
private List<CompanyData> companydataList(){
List<String> companyNames = sim.getCompanyNames();
List<Integer> companyValues = sim.getCompanyValues();
List<CompanyData> companydata = new ArrayList<>();
for (int i = 0 ; i < companyNames.size(); i++) {
String name = companyNames.get(i);
int value = companyValues.get(i);
CompanyData company = new CompanyData();
company.setPFCompanyName(name);
company.setPFShareValues(value);
companydata.add(company);
}
return companydata;
}
Obviously, it would be far more sensible to have sim, which I assume is some kind of data accessor, return a List<CompanyData> directly in the first place, instead of two different lists for the different properties.

How to remove duplicates from list<object>

I have a object in java called foo.
I can do foo.getDate(); and that will give me the date of one item.
But now I have a list<foo> and I want to get per item one date.
So if I loop through my list I will see this output:
3-1-2015
3-1-2015
5-1-2015
8-1-2015
8-1-2015
But I want to see:
3-1-2015
5-1-2015
8-1-2015
So I want that only the first item with a unique date will be added to the list.
How can I do this in Java?
Thanks in advance!
Probably the most easy way would be use a map (HashMap for example)... Use the Date as a key, then put all your Foo objects into it. Then every time a key already exists the value will be overwritten and you'll end up with only one Foo object per Date. If you need a list (for sorting, for example) you can do something like new ArrayList<Foo>( myMap.values() ); then.
Create Set which will store unique Dates. If date from your foo instance was not already added to set add this instance to list containing foo objects with unique dates.
List<Foo> list = new ArrayList<>();
//fill list with your foo instances
Set<Date> uniqueDates = new HashSet<>();
List<Foo> resultList = new ArrayList<>();
for (Foo f : list){
if (uniqueDates.add(f.getDate())){//if I was able to add date to set
//it means that instance with this date is seen first time
//so I can add it to result list
resultList.add(f);
}
}
well you probably should use Set for this.
Just to add to #Pshemo's answer, doing the same with Java 8 is straightforward:
public class RemoveDuplicates {
public static void main(String[] args) {
// Initialize some dates
long now = System.currentTimeMillis();
Date d1 = new Date(now);
Date d2 = new Date(now - 10_000_000_000L);
Date d3 = new Date(now - 100_000_000_000L);
// Initialize some Foos with the dates
List<Foo> list = new ArrayList<>(Arrays.asList(
new Foo(d3), new Foo(d3), new Foo(d2),
new Foo(d1), new Foo(d1)));
Set<Date> uniqueDates = new HashSet<>();
// Filter foos whose date is already in the set
List<Foo> distinct = list.stream().filter(
f -> uniqueDates.add(f.getDate())).
collect(Collectors.toList());
System.out.println(distinct); // [Foo [date=17/01/12],
// Foo [date=24/11/14],
// Foo [date=19/03/15]]
}
static class Foo {
private static DateFormat formatter = DateFormat.getDateInstance(DateFormat.SHORT);
private final Date date;
Date getDate() {
return this.date;
}
Foo(Date date) {
this.date = date;
}
#Override
public String toString() {
return "Foo [date=" + formatter.format(this.date) + "]";
}
}
}
The principle is exactly the same: if the date is already in the set, then the Foo is filtered from the stream.

Categories