Room SELECT Query for 1-n hashmap relationship - sqlite

I have the following ORM model:
Basically I want to track all public facilities of a state. So I have 1-n Relationship with a public facility like e.g. school:
public class StateWithFacilities {
#Embedded
State state;
#Relation(entity = School.class,parentColumn = "schoolId",entityColumn = "id")
ArrayList<School> allSchools;
}
The issue is that, I'm don't quite sure how to get allLocations which belong to a school, as a school has a HashMap of locations where the value represents to cost of the building.
My 1) idea looks like this:
public class StateWithFacilities {
#Embedded
State state;
#Relation(entity = School.class,parentColumn = "schoolId",entityColumn = "id")
ArrayList<SchoolWithLocations> allSchools;
}
public class SchoolWithLocations {
#Embedded
School school;
#Relation(entity = Location.class,parentColumn = "locationId",entityColumn = "id")
HashMap<Location, float> alllocationsWithCost;
}
The proposed idea wouldn't work, since I can't keep track of the cost of the building. What would the best solution be? I want to have the implemented, rather than creating a new table/entity class unless I don't have another option.

I think you might be causing yourself angst because of how you are handling the relationships.
Considering StateWithFacilities
You appear to be saying get the school who's id is equal to the schoolId column in the state.
While the realtionship should be from a column in the School that stores the appropriate StateId.
You appear to be using the reverse.
Example
Perhaps consider this example based upon what you appear to be wanting to do: (States, Schools and Locations have been given a name column to make the output easier to understand)-
State class (Entity and therefore Table) which is top of the hierarchy.
#Entity
public class State {
#PrimaryKey
Long stateId;
String stateName;
// etc //
public State(){}
#Ignore
public State(String stateName){
this.stateName = stateName;
}
.... getters and setters
}
School class (Entity and therefore Table) which will belong to a State.
#Entity(
foreignKeys = {
#ForeignKey(entity = State.class,parentColumns = "stateId",childColumns = "stateIdMap"),
},
indices = {#Index("stateIdMap")}
)
public class School {
#PrimaryKey
Long schoolId;
Long stateIdMap;
String schoolName;
// etc
public School(){}
#Ignore
public School(String schoolName, long stateId) {
this.schoolName = schoolName;
this.stateIdMap = stateId;
}
.... getters and setters
}
ForeignKeys aren't necessary but can assist with maintaining referential integrity.
Likewise the index on the stateIdMap column isn't required but if the ForeignKeys are defined Room issues a warning if the index doesn't exist.
Location class (Entity and therefore Table) which will belong to a School (a Scholl can have many Locations).
#Entity(
foreignKeys = {
#ForeignKey(entity = School.class,parentColumns = "schoolId",childColumns = "schoolIdMap")
},
indices = {#Index("schoolIdMap")}
)
public class Location {
#PrimaryKey
Long locationId;
Long schoolIdMap;
String locationName;
float x1;
float y1;
float x2;
float y2;
// etc
public Location(){}
#Ignore
public Location(String locationName,long schoolId, float x1, float y1, float x2, float y2) {
this.locationName = locationName;
this.schoolIdMap = schoolId;
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
.... getters and setters
}
To make the demo easier to read, Location has been given a name.
To cater for retrieving a parent with it's children the following POJO's are used :-
SchoolWithLocations
public class SchoolWithLocations {
#Embedded
School school;
#Relation(entity = Location.class,parentColumn = "schoolId",entityColumn = "schoolIdMap")
List<Location> locationList;
}
StateWithSchoolsWithLocations
public class StateWithSchoolsWithLocations {
#Embedded
State state;
#Relation(entity = School.class, parentColumn = "stateId",entityColumn = "stateIdMap")
List<SchoolWithLocations> schoolWithLocationsList;
}
A Dao AllDao with some common useful Dao's :-
#Dao
interface AllDao {
#Insert
long insert(State state);
#Insert
long[] insert(State...states);
#Insert
long insert(Location location);
#Insert
long[] insert(Location...locations);
#Insert
long insert(School school);
#Insert
long[] insert(School...schools);
#Query("SELECT * FROM State")
List<State> getAllStates();
#Query("SELECT * FROM State WHERE stateId=:stateId")
State getStateById(long stateId);
#Query("SELECT * FROM Location")
List<Location> getAllLocations();
#Query("SELECT * FROM Location WHERE locationId=:locationId")
Location getLocationById(long locationId);
#Query("SELECT * FROM Location WHERE x1=:x1 AND y1=:y1 AND x2=:x2 AND y2=:y2")
Location getLocationByCoords(float x1,float y1,float x2,float y2);
#Query("SELECT * FROM School")
List<School> getAllSchools();
#Transaction
#Query("SELECT * FROM State")
List<StateWithSchoolsWithLocations> getStateWithSchoolsAndLocations();
#Transaction
#Query("SELECT * FROM State WHERE stateId=:stateId")
List<StateWithSchoolsWithLocations> getStateByIdWithSchoolsAndLocations(long stateId);
}
A Database class TheDatabase
#Database(entities = {State.class,Location.class,School.class},exportSchema = false,version = 1)
abstract class TheDatabase extends RoomDatabase {
abstract AllDao getAllDao();
private static volatile TheDatabase instance;
public static TheDatabase getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(
context,
TheDatabase.class,
"state.db"
)
.allowMainThreadQueries()
.build();
}
return instance;
}
}
And finally and activity to demonstrate (run on the main thread) :-
public class MainActivity extends AppCompatActivity {
TheDatabase db;
AllDao dao;
static final String TAG = "StateINFO";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Instantiate Database and get dao
db = TheDatabase.getInstance(this);
dao = db.getAllDao();
// Add 3 states
long s1Id = dao.insert(new State("State1"));
long s2Id = dao.insert(new State("State2"));
// Add 2 Schools (in State1)
long sc1 = dao.insert(new School("School1 in State1",s1Id));
long sc2 = dao.insert(new School("School2 in State1",s1Id));
// Add 4 Locations
long l1Id = dao.insert(new Location("Loc1",sc1,1f,1f,2f,2f));
long l2Id = dao.insert(new Location("Loc2",sc1,2f,2f,3f,3f));
long l3Id = dao.insert(new Location("Loc3",sc1,3f,3f,4f,4f));
long l4Id = dao.insert(new Location("Loc4",sc2,4f,4f,5f,5f));
// Get Everything via State
for (StateWithSchoolsWithLocations swswl: dao.getStateWithSchoolsAndLocations() ) {
Log.d(TAG,"State is " + swswl.state.stateName);
for (SchoolWithLocations s: swswl.schoolWithLocationsList) {
Log.d(TAG,"\tSchool is " + s.school.schoolName);
for (Location l: s.locationList) {
Log.d(TAG,"\t\tLocation is " + l.locationName + " XYvalues are X1=" + l.x1 + " Y1=" + l.y2 + " X2=" + l.x2 + " Y2=" + l.y2);
}
}
}
}
}
Result
As can be seen it's easy to retrieve all the locations and the x1..Y2 values. The log, when the above is run, includes :-
2021-06-13 08:53:40.748 D/StateINFO: State is State1
2021-06-13 08:53:40.748 D/StateINFO: School is School1 in State1
2021-06-13 08:53:40.748 D/StateINFO: Location is Loc1 XYvalues are X1=1.0 Y1=2.0 X2=2.0 Y2=2.0
2021-06-13 08:53:40.748 D/StateINFO: Location is Loc2 XYvalues are X1=2.0 Y1=3.0 X2=3.0 Y2=3.0
2021-06-13 08:53:40.748 D/StateINFO: Location is Loc3 XYvalues are X1=3.0 Y1=4.0 X2=4.0 Y2=4.0
2021-06-13 08:53:40.748 D/StateINFO: School is School2 in State1
2021-06-13 08:53:40.748 D/StateINFO: Location is Loc4 XYvalues are X1=4.0 Y1=5.0 X2=5.0 Y2=5.0
2021-06-13 08:53:40.748 D/StateINFO: State is State2
I want to have the implemented, rather than creating a new table/entity class unless I don't have another option.
Whether or not the above could be used to make appropriate corrections to keep your current tables is something that you would have to determine.
Additional re HashMaps
The method added to SchoolWithLocations POJO :-
public HashMap<String,Float> getLocationsAsHashMap() {
HashMap<String,Float> rv = new HashMap<>();
for (Location l: locationList) {
String basekey = this.getClass().getSimpleName() + (rv.size() + 1);
rv.put(basekey+"x1",l.x1);
rv.put(basekey+ "y1",l.y1);
rv.put(basekey+"x2",l.x2);
rv.put(basekey+"y2",l.y2);
}
return rv;
}
The method added to School
public HashMap<String,Float> getLocationsAsHashMap(AllDao dao) {
HashMap<String,Float> rv = new HashMap<>();
for(Location l: dao.getLocationsBySchool(schoolId)) {
String basekey = this.getClass().getSimpleName() + (rv.size() + 1);
rv.put(basekey+"x1",l.x1);
rv.put(basekey+ "y1",l.y1);
rv.put(basekey+"x2",l.x2);
rv.put(basekey+"y2",l.y2);
}
return rv;
}
Notice the Subtle differences. As the School object does not contain the Locations then these need to be retrieved from the database. Hence, it needs an instance of the AllDao as it uses a dao to get the Locations.
AllDao
The following was added to AllDao to facilitate getting the applicable Locations for the School :-
#Query("SELECT * FROM location WHERE schoolIdMap=:schoolId")
List<Location> getLocationsBySchool(long schoolId);
The Amended loop that traverses the retrieved List of StateWithSchoolsWithLocations
// Get Everything via State
HashMap<String,Float> locations = new HashMap<>(); //<<<<< ADDED
HashMap<String,Float> locationsFromSchool = new HashMap<>(); //<<<<<ADDDED
for (StateWithSchoolsWithLocations swswl: dao.getStateWithSchoolsAndLocations() ) {
Log.d(TAG,"State is " + swswl.state.stateName);
for (SchoolWithLocations s: swswl.schoolWithLocationsList) {
Log.d(TAG,"\tSchool is " + s.school.schoolName);
for (Location l: s.locationList) {
Log.d(TAG,"\t\tLocation is " + l.locationName + " XYvalues are X1=" + l.x1 + " Y1=" + l.y2 + " X2=" + l.x2 + " Y2=" + l.y2);
}
/* ADDED get HashMap of Locations */
locations = s.getLocationsAsHashMap();
/* OR */
locationsFromSchool = s.school.getLocationsAsHashMap(dao);
Float value = 99.99999F; //<<<<< ADDED for setting a breakpoint
}
}
Result of the Amended Code
A breakpoint was added to the Line Float value = 99.99999F and run in debug mode.
When the Breakpoint was first hit (first StateWithSchoolsAndWithLocations) the debug window was :-
The Second Breakpoint :-

Related

Efficient way for Inheritance Table schemes

I'm planning to display an listview of CarServiceEntries.
The CarServiceEntry class contains basic data of a service:
#Entity
abstract class CarServiceEntry(
private int id;
private Date date;
private float odometer;
/*
getters and setters constructor....
*/
)
CarServiceEntry is abstract as the classes, which inherit it, have more detailied information:
#Entity
abstract class Income extends CarServiceEntry(
#Converter(....)
private PaymentType paymentType;
private float totalAmount;
/*
getters and setters constructor....
*/
)
The issue arrises with building the SQLScheme.
As I want to load all entries TOGETHER rather than making an own sqltable for each CarServiceEntry and query each table (getAllIncomes() getAllExpenses() etc), how can I load every CarServiceEntry(income,expense, service) using one sqltabletable for loading (if possible).
The current approach, which I'm not a fan of, looks like this:
CREATE TABLE CarServiceEntry(
id INTEGER PRIMARY KEY,
serviceType TEXT CHECK CONSTRAINT (....) //expense, income, service)
date,
odometer,
/*
A LOT of fields as each service has its own "unique" fields and I put it all together which I don' really like :( loading 30+ fields just for one sql statement is not something I like, unless I don't have any other option.
*/
)
I'd rather search for a solution like this:
CREATE TABLE CarServiceEntry(
id INTEGER PRIMARY KEY,
date DATE,
odometer NUMBER
)
CREATE TABLE Income INHERITS CarServiceEntry(
paymentType TEXT,
totalAmount NUMBER
/*some other fields*/
)
CREATE TABLE Expense INHERITS CarServiceEntry(
location TEXT
totalCost NUMBER
/*some other fields*/
)
==>
#Query("SELECT * FROM CarServiceEntry") //this should also return Income and Expense table
Flowable<List<CarServiceEntry>> getAllEntries();
Is there a way to it this way? Or is creating one table with a bulk of fields the only way?
According to: sql inheritance creating an FOREIGN KEY to my subentrytype tables would be kind of inheritance, however this does not solve my issue, as I would still have to need to load every table. I guess the only solution would be using one table with many null value fields?
SQLite doesn't support inheritance and I believe that it will be as simple, if not simpler, to utilise relationships which SQLite and Room support.
Creating multiple tables via room is pretty easy as is creating and handling relationships. So I would suggest taking the typical approach.
Here's an example based upon what I think that yo are trying to accomplish.
First the CarServiceEntry table (which will later have Expenses and Incomes related to it) :-
public class CarServiceEntry {
#PrimaryKey
private Long id;
private String date;
private float odometer;
public CarServiceEntry(){}
.... getters and setters removed for brevity
/* more convenient constructor (see cse3 in example) */
/* #Ignore to supress Room warning */
#Ignore
public CarServiceEntry(String date, Float odometer) {
this.date = date;
this.odometer = odometer;
}
}
note simplified so TypeConverters aren't required
Next the income table :-
#Entity(tableName = "income",
foreignKeys = {
#ForeignKey(
entity = CarServiceEntry.class,
parentColumns = "id",
childColumns = "incomeCarServiceEntryId",
onDelete = CASCADE,
onUpdate = CASCADE
)
},
indices = {#Index(
value = {"incomeCarServiceEntryId"}
)}
)
public class Income {
#PrimaryKey
private Long incomeId;
private Long incomeCarServiceEntryId;
private int paymentType;
private float totalAmount;
.... getters and setters
}
note foreign keys = { .... } nor indicies = { .... } are required but suggested that they be used as the help to ensure referential integrity
Note the additional column incomeCarServiceEntryId this is the ID of the related CarServiceEntry.
Next the expense table (pretty similar to income table):-
#Entity(tableName = "expense",
foreignKeys = {
#ForeignKey(
entity = CarServiceEntry.class,
parentColumns = {"id"},
childColumns = {"expenseCarServiceEntryId"},
onDelete = CASCADE,
onUpdate = CASCADE
)
},
indices = {
#Index(
value = {"expenseCarServiceEntryId"}
)}
)
public class Expense {
#PrimaryKey
private Long expenseId;
private long expenseCarServiceEntryId;
private String location;
private float totalCost;
.... getters and setters
}
Now a POJO (not a table) for extracting the related data (i.e CarServiceEntry with all of the related Incomes and all the related Expenses) named CarServiceEntryWithIncomeWithExpense :-
public class CarServiceEntryWithIncomeWithExpense {
#Embedded
CarServiceEntry carServiceEntry;
#Relation(entity = Income.class,parentColumn = "id",entityColumn = "incomeCarServiceEntryId")
List<Income> incomeList;
#Relation(entity = Expense.class,parentColumn = "id",entityColumn = "expenseCarServiceEntryId")
List<Expense> expenseList;
}
YES that's it
Now the Dao's (All in One) AllDao :-
#Dao
interface AllDao {
#Insert
long insert(CarServiceEntry carServiceEntry);
#Insert
long insert(Expense expense);
#Insert
long insert(Income income);
#Query("SELECT * FROM car_service_entry")
List<CarServiceEntryWithIncomeWithExpense> getAllCarServiceEntriesWithIncomesAndWithExpenses();
}
The #Database (includes singleton approach) named Database (probably better to use another name) :-
#Database(entities = {CarServiceEntry.class,Income.class,Expense.class},version = 1)
public abstract class Database extends RoomDatabase {
abstract AllDao getAllDao();
private static volatile Database instance;
public static Database getInstance(Context context) {
if (instance == null) {
instance = Room.databaseBuilder(context,Database.class,"carservice.db")
.allowMainThreadQueries()
.build();
}
return instance;
}
}
Note for brevity and convenience of the demo, the main thread is used.
Finally putting it all together and demonstrating MainActivity.
The demo adds 3 carServiceEntries, with incomes and expenses (the 3rd has none of either). It then extracts the 3 carServiceEntries with all the incomes and expenses. Traversing the extracted and outputting what has been extracted to the log.
:-
public class MainActivity extends AppCompatActivity {
Database db;
AllDao dao;
private static final String TAG = "CSEINFO";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
db = Database.getInstance(this);
dao = db.getAllDao();
/* Add a Service Entry noting it's ID */
CarServiceEntry cse1 = new CarServiceEntry();
cse1.setDate("2021-06-01");
cse1.setOdometer(5120.78F);
long cse1Id = dao.insert(cse1);
/* Add another Service Entry noting it's id */
CarServiceEntry cse2 = cse1;
cse2.setDate("2021-06-02");
cse2.setOdometer(7065.83F);
long cse2Id = dao.insert(cse2);
/* Use a single Income to add 3 Income Entries for (linked to) the 1st Service Entry */
Income incomeEntry = new Income();
incomeEntry.setIncomeCarServiceEntryId(cse1Id);
incomeEntry.setPaymentType(10);
incomeEntry.setTotalAmount(120.00F);
dao.insert(incomeEntry);
incomeEntry.setPaymentType(20);
incomeEntry.setTotalAmount(230.00F);
dao.insert(incomeEntry);
incomeEntry.setPaymentType(15);
incomeEntry.setTotalAmount(75.55F);
dao.insert(incomeEntry);
/* Use the same Income Entry to add 1 Entry for the 2nd Service Entry */
incomeEntry.setIncomeCarServiceEntryId(cse2Id);
incomeEntry.setPaymentType(25);
incomeEntry.setTotalAmount(134.56F);
dao.insert(incomeEntry);
/* Add some Expense Entries */
Expense expenseEntry = new Expense();
expenseEntry.setExpenseCarServiceEntryId(cse1Id);
expenseEntry.setLocation("London");
expenseEntry.setTotalCost(500.00F);
dao.insert(expenseEntry);
expenseEntry.setLocation("New York");
expenseEntry.setTotalCost(60.66F);
dao.insert(expenseEntry);
expenseEntry.setExpenseCarServiceEntryId(cse2Id);
expenseEntry.setLocation("Paris");
dao.insert(expenseEntry);
expenseEntry.setLocation("Hamburg");
dao.insert(expenseEntry);
expenseEntry.setLocation("Madrid");
dao.insert(expenseEntry);
dao.insert(new CarServiceEntry("2021-06-03",1765.34F));
for (CarServiceEntryWithIncomeWithExpense cse: dao.getAllCarServiceEntriesWithIncomesAndWithExpenses()) {
Log.d(
TAG,
"CSE ID = " + cse.carServiceEntry.getId() +
" Date = " + cse.carServiceEntry.getDate() +
" ODO = " + cse.carServiceEntry.getOdometer()
);
for (Income i: cse.incomeList) {
Log.d(
TAG,
"\tIncome Payment Type is " + i.getPaymentType() + " Total is " + i.getTotalAmount()
);
}
for(Expense e: cse.expenseList) {
Log.d(
TAG,
"\tExpense Location is " + e.getLocation() + " Total is " + e.getTotalCost()
);
}
}
}
}
Result
The following is output to the Log :-
2021-06-11 13:01:35.116 D/CSEINFO: CSE ID = 1 Date = 2021-06-01 ODO = 5120.78
2021-06-11 13:01:35.116 D/CSEINFO: Income Payment Type is 10 Total is 120.0
2021-06-11 13:01:35.116 D/CSEINFO: Income Payment Type is 20 Total is 230.0
2021-06-11 13:01:35.116 D/CSEINFO: Income Payment Type is 15 Total is 75.55
2021-06-11 13:01:35.116 D/CSEINFO: Expense Location is London Total is 500.0
2021-06-11 13:01:35.116 D/CSEINFO: Expense Location is New York Total is 60.66
2021-06-11 13:01:35.116 D/CSEINFO: CSE ID = 2 Date = 2021-06-02 ODO = 7065.83
2021-06-11 13:01:35.117 D/CSEINFO: Income Payment Type is 25 Total is 134.56
2021-06-11 13:01:35.117 D/CSEINFO: Expense Location is Paris Total is 60.66
2021-06-11 13:01:35.117 D/CSEINFO: Expense Location is Hamburg Total is 60.66
2021-06-11 13:01:35.117 D/CSEINFO: Expense Location is Madrid Total is 60.66
2021-06-11 13:01:35.117 D/CSEINFO: CSE ID = 3 Date = 2021-06-03 ODO = 1765.34

Aggregation function on result table consisting of data from multiple tables using Speedment

I have completely no idea how to translate this sql query into Java Streams using the Speedment Framework. The “result table” in Java could be of any type (it could be Map or even a specially defined user class).
Here’s the sql query I’m trying to translate into Java Streams:
SELECT d.dept_name, AVG(s.salary)
FROM employees e
JOIN salaries s
ON e.emp_no=s.emp_no
JOIN dept_emp de ON de.emp_no = e.emp_no
JOIN departments d ON d.dept_no=de.dept_no
WHERE s.to_date = '9999-01-01' AND de.to_date = '9999-01-01'
GROUP BY d.dept_name;
DB Scheme which I'm using
Source: https://github.com/datacharmer/test_db/tree/master/images
Thanks in advance.
Thanks to the Speedment channel on Youtube I have found a solution.
1) Create a “Result/Wrapper” class for aggregation:
private static class Result {
private Double salary;
private String departmentName;
public Double getSalary() { return salary; }
public void setSalary(Double salary) { this.salary = salary; }
public String getDepartmentName() { return departmentName; }
public void setDepartmentName(String departmentName) {
this.departmentName = departmentName;
}
#Override
public String toString() {
return "Result{" +
"salary=" + salary +
", departmentName='" + departmentName + '\'' +
'}';
}
}
2) Create a join object:
Join<Tuple2<Departments, Salaries>> join = app.getOrThrow(JoinComponent.class)
.from(EmployeesManager.IDENTIFIER)
.innerJoinOn(DeptEmp.EMP_NO).equal(Employees.EMP_NO)
.where(DeptEmp.TO_DATE.equal(Date.valueOf("9999-01-01")))
.innerJoinOn(Departments.DEPT_NO).equal(DeptEmp.DEPT_NO)
.innerJoinOn(Salaries.EMP_NO).equal(Employees.EMP_NO)
.where(Salaries.TO_DATE.equal(Date.valueOf("9999-01-01")))
.build((a,b,c,d) -> Tuples.of(c,d));
3) Create rules for aggregation:
AggregationCollector<Tuple2<Departments, Salaries>, ?, Result> aggregationCollector = Aggregator.builder(Result::new)
.firstOn(Tuple2.<Departments, Salaries>getter0())
.andThen(Departments.DEPT_NAME)
.key(Result::setDepartmentName)
.firstOn(Tuple2.getter1())
.andThen(Salaries.SALARY)
.average(Result::setSalary)
.build()
.createCollector();
4) Create an aggregation object:
Aggregation<Result> aggregation = join.stream().collect(aggregationCollector);
5) Do whatever you have to do with it:
aggregation.streamAndClose().forEachOrdered(System.out::println);
Output:
Result{salary=67657.91955820487, departmentName='Development'}
Result{salary=78559.93696229013, departmentName='Finance'}
Result{salary=67843.30198484214, departmentName='Production'}
Result{salary=80058.84880743832, departmentName='Marketing'}
Result{salary=63921.89982943111, departmentName='Human Resources'}
Result{salary=88852.96947030538, departmentName='Sales'}
Result{salary=67285.23017815467, departmentName='Customer Service'}
Result{salary=67913.3749757136, departmentName='Research'}
Result{salary=65441.99340024768, departmentName='Quality Management'}

What's the Java 8 equivalent of zipWithIndex method in Scala List? [duplicate]

Is there a concise way to iterate over a stream whilst having access to the index in the stream?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
which seems rather disappointing compared to the LINQ example given there
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Is there a more concise way?
Further it seems the zip has either moved or been removed...
The cleanest way is to start from a stream of indices:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0, names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(Collectors.toList());
The resulting list contains "Erik" only.
One alternative which looks more familiar when you are used to for loops would be to maintain an ad hoc counter using a mutable object, for example an AtomicInteger:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
AtomicInteger index = new AtomicInteger();
List<String> list = Arrays.stream(names)
.filter(n -> n.length() <= index.incrementAndGet())
.collect(Collectors.toList());
Note that using the latter method on a parallel stream could break as the items would not necesarily be processed "in order".
The Java 8 streams API lacks the features of getting the index of a stream element as well as the ability to zip streams together. This is unfortunate, as it makes certain applications (like the LINQ challenges) more difficult than they would be otherwise.
There are often workarounds, however. Usually this can be done by "driving" the stream with an integer range, and taking advantage of the fact that the original elements are often in an array or in a collection accessible by index. For example, the Challenge 2 problem can be solved this way:
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList =
IntStream.range(0, names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(toList());
As I mentioned above, this takes advantage of the fact that the data source (the names array) is directly indexable. If it weren't, this technique wouldn't work.
I'll admit that this doesn't satisfy the intent of Challenge 2. Nonetheless it does solve the problem reasonably effectively.
EDIT
My previous code example used flatMap to fuse the filter and map operations, but this was cumbersome and provided no advantage. I've updated the example per the comment from Holger.
Since guava 21, you can use
Streams.mapWithIndex()
Example (from official doc):
Streams.mapWithIndex(
Stream.of("a", "b", "c"),
(str, index) -> str + ":" + index)
) // will return Stream.of("a:0", "b:1", "c:2")
I've used the following solution in my project. I think it is better than using mutable objects or integer ranges.
import java.util.*;
import java.util.function.*;
import java.util.stream.Collector;
import java.util.stream.Collector.Characteristics;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static java.util.Objects.requireNonNull;
public class CollectionUtils {
private CollectionUtils() { }
/**
* Converts an {#link java.util.Iterator} to {#link java.util.stream.Stream}.
*/
public static <T> Stream<T> iterate(Iterator<? extends T> iterator) {
int characteristics = Spliterator.ORDERED | Spliterator.IMMUTABLE;
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, characteristics), false);
}
/**
* Zips the specified stream with its indices.
*/
public static <T> Stream<Map.Entry<Integer, T>> zipWithIndex(Stream<? extends T> stream) {
return iterate(new Iterator<Map.Entry<Integer, T>>() {
private final Iterator<? extends T> streamIterator = stream.iterator();
private int index = 0;
#Override
public boolean hasNext() {
return streamIterator.hasNext();
}
#Override
public Map.Entry<Integer, T> next() {
return new AbstractMap.SimpleImmutableEntry<>(index++, streamIterator.next());
}
});
}
/**
* Returns a stream consisting of the results of applying the given two-arguments function to the elements of this stream.
* The first argument of the function is the element index and the second one - the element value.
*/
public static <T, R> Stream<R> mapWithIndex(Stream<? extends T> stream, BiFunction<Integer, ? super T, ? extends R> mapper) {
return zipWithIndex(stream).map(entry -> mapper.apply(entry.getKey(), entry.getValue()));
}
public static void main(String[] args) {
String[] names = {"Sam", "Pamela", "Dave", "Pascal", "Erik"};
System.out.println("Test zipWithIndex");
zipWithIndex(Arrays.stream(names)).forEach(entry -> System.out.println(entry));
System.out.println();
System.out.println("Test mapWithIndex");
mapWithIndex(Arrays.stream(names), (Integer index, String name) -> index+"="+name).forEach((String s) -> System.out.println(s));
}
}
In addition to protonpack, jOOλ's Seq provides this functionality (and by extension libraries that build on it like cyclops-react, I am the author of this library).
Seq.seq(Stream.of(names)).zipWithIndex()
.filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
.toList();
Seq also supports just Seq.of(names) and will build a JDK Stream under the covers.
The simple-react equivalent would similarly look like
LazyFutureStream.of(names)
.zipWithIndex()
.filter( namesWithIndex -> namesWithIndex.v1.length() <= namesWithIndex.v2 + 1)
.toList();
The simple-react version is more tailored for asynchronous / concurrent processing.
Just for completeness here's the solution involving my StreamEx library:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
EntryStream.of(names)
.filterKeyValue((idx, str) -> str.length() <= idx+1)
.values().toList();
Here we create an EntryStream<Integer, String> which extends Stream<Entry<Integer, String>> and adds some specific operations like filterKeyValue or values. Also toList() shortcut is used.
I found the solutions here when the Stream is created of list or array (and you know the size). But what if Stream is with unknown size? In this case try this variant:
public class WithIndex<T> {
private int index;
private T value;
WithIndex(int index, T value) {
this.index = index;
this.value = value;
}
public int index() {
return index;
}
public T value() {
return value;
}
#Override
public String toString() {
return value + "(" + index + ")";
}
public static <T> Function<T, WithIndex<T>> indexed() {
return new Function<T, WithIndex<T>>() {
int index = 0;
#Override
public WithIndex<T> apply(T t) {
return new WithIndex<>(index++, t);
}
};
}
}
Usage:
public static void main(String[] args) {
Stream<String> stream = Stream.of("a", "b", "c", "d", "e");
stream.map(WithIndex.indexed()).forEachOrdered(e -> {
System.out.println(e.index() + " -> " + e.value());
});
}
With a List you can try
List<String> strings = new ArrayList<>(Arrays.asList("First", "Second", "Third", "Fourth", "Fifth")); // An example list of Strings
strings.stream() // Turn the list into a Stream
.collect(HashMap::new, (h, o) -> h.put(h.size(), o), (h, o) -> {}) // Create a map of the index to the object
.forEach((i, o) -> { // Now we can use a BiConsumer forEach!
System.out.println(String.format("%d => %s", i, o));
});
Output:
0 => First
1 => Second
2 => Third
3 => Fourth
4 => Fifth
If you happen to use Vavr(formerly known as Javaslang), you can leverage the dedicated method:
Stream.of("A", "B", "C")
.zipWithIndex();
If we print out the content, we will see something interesting:
Stream((A, 0), ?)
This is because Streams are lazy and we have no clue about next items in the stream.
Here is code by abacus-common
Stream.of(names).indexed()
.filter(e -> e.value().length() <= e.index())
.map(Indexed::value).toList();
Disclosure: I'm the developer of abacus-common.
There isn't a way to iterate over a Stream whilst having access to the index because a Stream is unlike any Collection. A Stream is merely a pipeline for carrying data from one place to another, as stated in the documentation:
No storage. A stream is not a data structure that stores elements; instead, they carry values from a source (which could be a data structure, a generator, an IO channel, etc) through a pipeline of computational operations.
Of course, as you appear to be hinting at in your question, you could always convert your Stream<V> to a Collection<V>, such as a List<V>, in which you will have access to the indexes.
With https://github.com/poetix/protonpack
u can do that zip:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = IntStream.range(0, names.length).boxed();
nameList = StreamUtils.zip(indices, stream(names),SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey()).map(Entry::getValue).collect(toList());
System.out.println(nameList);
If you don't mind using a third-party library, Eclipse Collections has zipWithIndex and forEachWithIndex available for use across many types. Here's a set of solutions to this challenge for both JDK types and Eclipse Collections types using zipWithIndex.
String[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
ImmutableList<String> expected = Lists.immutable.with("Erik");
Predicate<Pair<String, Integer>> predicate =
pair -> pair.getOne().length() <= pair.getTwo() + 1;
// JDK Types
List<String> strings1 = ArrayIterate.zipWithIndex(names)
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings1);
List<String> list = Arrays.asList(names);
List<String> strings2 = ListAdapter.adapt(list)
.zipWithIndex()
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings2);
// Eclipse Collections types
MutableList<String> mutableNames = Lists.mutable.with(names);
MutableList<String> strings3 = mutableNames.zipWithIndex()
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings3);
ImmutableList<String> immutableNames = Lists.immutable.with(names);
ImmutableList<String> strings4 = immutableNames.zipWithIndex()
.collectIf(predicate, Pair::getOne);
Assert.assertEquals(expected, strings4);
MutableList<String> strings5 = mutableNames.asLazy()
.zipWithIndex()
.collectIf(predicate, Pair::getOne, Lists.mutable.empty());
Assert.assertEquals(expected, strings5);
Here's a solution using forEachWithIndex instead.
MutableList<String> mutableNames =
Lists.mutable.with("Sam", "Pamela", "Dave", "Pascal", "Erik");
ImmutableList<String> expected = Lists.immutable.with("Erik");
List<String> actual = Lists.mutable.empty();
mutableNames.forEachWithIndex((name, index) -> {
if (name.length() <= index + 1)
actual.add(name);
});
Assert.assertEquals(expected, actual);
If you change the lambdas to anonymous inner classes above, then all of these code examples will work in Java 5 - 7 as well.
Note: I am a committer for Eclipse Collections
You can use IntStream.iterate() to get the index:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i < names.length, i -> i + 1)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(Collectors.toList());
This only works for Java 9 upwards in Java 8 you can use this:
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList = IntStream.iterate(0, i -> i + 1)
.limit(names.length)
.filter(i -> names[i].length() <= i)
.mapToObj(i -> names[i])
.collect(Collectors.toList());
If you are trying to get an index based on a predicate, try this:
If you only care about the first index:
OptionalInt index = IntStream.range(0, list.size())
.filter(i -> list.get(i) == 3)
.findFirst();
Or if you want to find multiple indexes:
IntStream.range(0, list.size())
.filter(i -> list.get(i) == 3)
.collect(Collectors.toList());
Add .orElse(-1); in case you want to return a value if it doesn't find it.
One possible way is to index each element on the flow:
AtomicInteger index = new AtomicInteger();
Stream.of(names)
.map(e->new Object() { String n=e; public i=index.getAndIncrement(); })
.filter(o->o.n.length()<=o.i) // or do whatever you want with pairs...
.forEach(o->System.out.println("idx:"+o.i+" nam:"+o.n));
Using an anonymous class along a stream is not well-used while being very useful.
If you need the index in the forEach then this provides a way.
public class IndexedValue {
private final int index;
private final Object value;
public IndexedValue(final int index, final Object value) {
this.index = index;
this.value = value;
}
public int getIndex() {
return index;
}
public Object getValue() {
return value;
}
}
Then use it as follows.
#Test
public void withIndex() {
final List<String> list = Arrays.asList("a", "b");
IntStream.range(0, list.size())
.mapToObj(index -> new IndexedValue(index, list.get(index)))
.forEach(indexValue -> {
System.out.println(String.format("%d, %s",
indexValue.getIndex(),
indexValue.getValue().toString()));
});
}
you don't need a map necessarily
that is the closest lambda to the LINQ example:
int[] idx = new int[] { 0 };
Stream.of(names)
.filter(name -> name.length() <= idx[0]++)
.collect(Collectors.toList());
You can create a static inner class to encapsulate the indexer as I needed to do in example below:
static class Indexer {
int i = 0;
}
public static String getRegex() {
EnumSet<MeasureUnit> range = EnumSet.allOf(MeasureUnit.class);
StringBuilder sb = new StringBuilder();
Indexer indexer = new Indexer();
range.stream().forEach(
measureUnit -> {
sb.append(measureUnit.acronym);
if (indexer.i < range.size() - 1)
sb.append("|");
indexer.i++;
}
);
return sb.toString();
}
This question (Stream Way to get index of first element matching boolean) has marked the current question as a duplicate, so I can not answer it there; I am answering it here.
Here is a generic solution to get the matching index that does not require an external library.
If you have a list.
public static <T> int indexOf(List<T> items, Predicate<T> matches) {
return IntStream.range(0, items.size())
.filter(index -> matches.test(items.get(index)))
.findFirst().orElse(-1);
}
And call it like this:
int index = indexOf(myList, item->item.getId()==100);
And if using a collection, try this one.
public static <T> int indexOf(Collection<T> items, Predicate<T> matches) {
int index = -1;
Iterator<T> it = items.iterator();
while (it.hasNext()) {
index++;
if (matches.test(it.next())) {
return index;
}
}
return -1;
}
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
String completeString
= IntStream.range(0,namesArray.length)
.mapToObj(i -> namesArray[i]) // Converting each array element into Object
.map(String::valueOf) // Converting object to String again
.collect(Collectors.joining(",")); // getting a Concat String of all values
System.out.println(completeString);
OUTPUT : Sam,Pamela,Dave,Pascal,Erik
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
IntStream.range(0,namesArray.length)
.mapToObj(i -> namesArray[i]) // Converting each array element into Object
.map(String::valueOf) // Converting object to String again
.forEach(s -> {
//You can do various operation on each element here
System.out.println(s);
}); // getting a Concat String of all
To Collect in the List:
String[] namesArray = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> namesList
= IntStream.range(0,namesArray.length)
.mapToObj(i -> namesArray[i]) // Converting each array element into Object
.map(String::valueOf) // Converting object to String again
.collect(Collectors.toList()); // collecting elements in List
System.out.println(listWithIndex);
As jean-baptiste-yunès said, if your stream is based on a java List then using an AtomicInteger and its incrementAndGet method is a very good solution to the problem and the returned integer does correspond to the index in the original List as long as you do not use a parallel stream.
Here's solution for standard Java:
In-line solution:
Arrays.stream("zero,one,two,three,four".split(","))
.map(new Function<String, Map.Entry<Integer, String>>() {
int index;
#Override
public Map.Entry<Integer, String> apply(String s) {
return Map.entry(index++, s);
}
})
.forEach(System.out::println);
and more readable solution with utility method:
static <T> Function<T, Map.Entry<Integer, T>> mapWithIntIndex() {
return new Function<T, Map.Entry<Integer, T>>() {
int index;
#Override
public Map.Entry<Integer, T> apply(T t) {
return Map.entry(index++, t);
}
};
}
...
Arrays.stream("zero,one,two,three,four".split(","))
.map(mapWithIntIndex())
.forEach(System.out::println);
If the list is unique, we can make use of indexOf method.
List<String> names = Arrays.asList("Sam", "Pamela", "Dave", "Pascal", "Erik");
names.forEach(name ->{
System.out.println((names.indexOf(name) + 1) + ": " + name);
});

Setting up TableColumns Value using Generic Types

I wanted to program a TableBrowser for a MYSQl Database in JavaFX.
My first problem is: i dont know which types i get back from the Database.
So i decided to wrap those types with a Wrapper-class.
To show these values on the GUI, i used the TableColumns setCellValueFactory-method, which
needs a value, that implements ObservableValue.
So i tried to implement the ObservableValue-interface.
But when i run the program it doesnt show the right Values.
TableBrowser after connecting to the Database
Has anyone an idea where i did wrong or knows a more recommended way to implement it ?
Here is the Part of the Code from the TableBrowser
/*
* this variable is used to iterate over the tableview's columns.
* It is a class variable, because it is not possible (for some reasons)
* to use a local variable while working with it in the context of Lambda-expressions
*/
int t = 0;
// those two variables are defined in the class Body
private final TableView<Entry> tableview = new TableView<>();
private final ObservableList<Entry> columndata = FXCollections.observableArrayList();
// the following Code is inside the Button's Actionlistener
for(int i = 1; i <= maxcol; i++) // adds a new TableColum for every colum in the DB
{
tableview.getColumns().add(new TableColumn<Entry, String>rsmd.getColumnName(i)));
}
// iterates over the ResultSet
while(rs.next())
{
// this is the dataset i put in my TableView
Entry row = new Entry(maxcol);
// for each Column i add the columnvalue to the current dataset
for(int i = 1; i <= maxcol; i++)
{
int type = rsmd.getColumnType(i);
Object value = rs.getObject(i);
row.setCellValue(i-1, type, value);
}
// adds a new dataset to the ObservableList<Entry>
columndata.add(row);
}
// puts all datasets in the TableView
tableview.setItems(columndata);
// iterates over all Columns
for(t = 0; t < tableview.getColumns().size(); t++)
{
// should set the CellValueFactory for each Column so it shows the data
/*
* I apologise if there a horrible mistake.
* I never worked with Lamda before and just copied it form an example page :)
*/
tableview.getColumns().get(t).setCellValueFactory(celldata -> celldata.getValue().getCellValue(t-1));
}
This is my Entry class, which is an inner Class in TableBrowserclass
/*
* should represent a Dataset.
* Has an array, which holdes every columnvalue as a WrapperType
*/
private class Entry
{
WrapperType<?>[] columns;
private Entry(int columncount)
{
columns = new WrapperType[columncount];
}
private WrapperType<?> getCellValue(int col)
{
return columns[col];
}
private void setCellValue(int col, int type, Object value)
{
columns[col] = MySQLTypeWrapper.getInstance().wrapType(type, value);
}
}
Here is the MySQLTypeWrapper class, which holds the WrapperType as an inner class
public class MySQLTypeWrapper
{
public WrapperType<?> wrapType(int type, Object Value)
{
Class<?> typeclass = toClass(type);
return new WrapperType<>(typeclass.cast(Value));
}
/*
* returns the appropriate class def for every database type
* Expl: VARCHAR returns String.class
*/
private static Class<?> toClass(int type) {...}
/*
* I copied the content of the of the overridden Methods from StringPropertyBase
* as i have clue how to implement ObservableValue
*/
class WrapperType<T> implements ObservableValue<WrapperType<T>>
{
private T value;
private ExpressionHelper<WrapperType<T>> helper = null;
private WrapperType(T value)
{
this.value = value;
}
#Override
public void addListener(InvalidationListener listener)
{
helper = ExpressionHelper.addListener(helper, this, listener);
}
#Override
public void removeListener(InvalidationListener listener)
{
helper = ExpressionHelper.removeListener(helper, listener);
}
#Override
public void addListener(ChangeListener<? super WrapperType<T>> listener)
{
helper = ExpressionHelper.addListener(helper, this, listener);
}
#Override
public void removeListener(ChangeListener<? super WrapperType<T>> listener)
{
helper = ExpressionHelper.removeListener(helper, listener);
}
#Override
public WrapperType<T> getValue()
{
return this;
}
public String toString()
{
return value.toString();
}
}
}
Thanks for your help in advance :)
As mentioned in the comments, your first problem was not using the TableView's Items property.
For the second part - one solution would be to create a helper method along the lines of
private <T> Callback<TableColumn.CellDataFeatures<Entry,T>,ObservableValue<T>> createCellFactory(int columnIndex) {
return celldata -> celldata.getValue().getCellValue(columnIndex);
}
and then change the loop to
// Now t can be a local variable, as it is not directly passed to the lambda.
for(int t = 0; t < tableview.getColumns().size(); t++)
{
// should set the CellValueFactory for each Column so it shows the data
tableview.getColumns().get(t).setCellValueFactory(createCellFactory(t));
}
Note that this time the variable passed to the lambda is a local effectively-final variable and not an instance variable, so the lambda is created with the correct value every time.
One last word of advice - are you sure you need this amount of generality? What I mean is - it is usually better to create a class to directly represent your DB structure with proper getters and setters, then you can use PropertyValueFactory.

java 8 stream group by and summing double

I am very new to streams in java 8 so my approach could be wrong.
I have 2 objects as follows
object1 {
BigDecimal amount;
Code1 code1;
Code2 code2;
Code3 code3;
String desc;
}
object2 {
BigDecimal amount;
Code1 code1;
Code2 code2;
Code3 code3;
}
So I want to collect all object1 where code1 && code2 && code3 are same and then sum the amount add it to object2 list.
I do not have a code to do it...I want to write a code that does the job
I am trying to implement something from http://docs.oracle.com/javase/tutorial/collections/interfaces/map.html
Or compute the sum of all salaries by department:
// Compute sum of salaries by department
Map<Department, Integer> totalByDept = employees.stream()
.collect(Collectors.groupingBy(Employee::getDepartment,
Collectors.summingInt(Employee::getSalary)));
Thanks to JB Nizet for pointing me in the right direction.
I had to modify my object2
public class CodeSummary {
Double amount;
CodeKey key;
//getters and setters
}
public class CodeKey {
String code1;
String code2;
String code3;
//getters and setters
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof CodeKey)) return false;
CodeKey that = (CodeKey) o;
if (!code1.equals(that.code1)) return false;
if (!code2.equals(that.code2)) return false;
if (!code3.equals(that.code3)) return false;
return true;
}
#Override
public int hashCode() {
int result = code1.hashCode();
result = 31 * result + code2.hashCode();
result = 31 * result + code3.hashCode();
return result;
}
}
iterate over object1 and populate object2. Once i had my object2 (now codeSymmary) populated. i could use the method below to do the job.
Map<CodeKey, Double> summaryMap = summaries.parallelStream().
collect(Collectors.groupingBy(CodeSummary::getKey,
Collectors.summingDouble(CodeSummary::getAmount))); // summing the amount of grouped codes.
If anyone is using this as an example. then make sure you override the equal and hashcode function in your key object. else grouping will not work.
Hope this helps someone

Resources