I am looking for an example to filter two lists and create a list that contains only a subset based on condition. For example
List-1 contains list of User objects
List-2 contains list of PaidUser
Now I need to filter and prepare a list of unpaid users. Below code produce list with all elements but expectation is only "ABC" . Please assist
class User {
private String userName;
private String age;
public String getAge(){
return age;
}
public String getUserName(){
return userName;
}
}
class PaidUser{
private String userName;
private double amt;
public String getUserName(){
return userName;
}
}
List<User> users = new ArrayList<>();
User u = new User();
u.age = "12";
u.userName = "XYZ";
users.add(u);
u.age = "12";
u.userName = "ABC";
users.add(u);
List<PaidUser> paids = new ArrayList<>();
PaidUser paid = new PaidUser();
paid.userName = "XYZ";
paid.amt = 1;
paids.add(paid);
List<User> unpaidUsers = users.stream()
.filter(e -> (!paids.stream()
.anyMatch(p-> p.getUserName().equals(e.getUserName()))))
.collect(Collectors.toList());
The issue is not with your logic rather in the way you add User to your input list.
After you add an object, you mutate the same object and add the same reference
users.add(u);
u.age = "12";
u.userName = "ABC";
users.add(u);
After executing the above lines your list has the same User reference twice (User with name ABC and age 12).
Change it to
User u = new User();
u.age = "12";
u.userName = "XYZ";
users.add(u);
u = new User(); //Create new User object
u.age = "12";
u.userName = "ABC";
users.add(u);
Related
I have my one table like UserTable.
#Entity
public class UserTable{
#PrimaryKey(autoGenerate = true)
private int userId;
private String userName;
private String userEmailId;
// Below code is getter and setter of this class.
}
#Dao
public interface UserDao {
#Query("SELECT * FROM userTable")
public List<UserTable> loadAllUsers();
#Insert
public long insertUserTable(UserTable userTable);
#Insert
public long[] insertUserTables(UserTable... userTables);
#Update
public int updateUserTable(UserTable userTable);
#Delete
public int deleteUserTable(UserTable userTable);
#RawQuery
public abstract List<UserTable> loadAllUserListByGivenIds
(SupportSQLiteQuery query);
public default List<UserTable> loadAllUserListByIds(long[] userIds) {
List<UserTable> list;
ArrayList<Object> argsList = new ArrayList<>();
String selectQuery = "SELECT * FROM UserTable WHERE userId IN (?);";
argsList.add(userIds);
SimpleSQLiteQuery simpleSQLiteQuery = new SimpleSQLiteQuery(selectQuery, argsList.toArray());
list = loadAllUserListByGivenIds(simpleSQLiteQuery);
return list;
}
}
// Now in My MainActivity.class file, I have use following code:
List<UserTable> userList= databaseClient
.getAppDatabase()
.userDao()
.loadAllUserListByIds(new long[]{1L,2L});
My query is running in normal database, but when I was pass array of user ids then, in #RawQuery() method of dao class is not supported for "IN" clause used in where condition "WHERE userId IN (?)".
How, I will use "IN" clause in #RawQuery() of room database.
Much easier to use an #Query it's as simple as:-
#Query("SELECT * FROM UserTable WHERE userId IN (:idList)")
public List<UserTable> getWhatever(long[] idList);
You'd then use getWhatever(new long[]{1L,2L})
If you need it an #rawQuery though you could do it like (used previous answer code for my convenience) :-
private List<TableXEntity> loadAllUserListByIds(int order,long[] idList) {
StringBuilder idListAsCSV = new StringBuilder(); //<<<<<<<<<<
boolean afterFirst = false; //<<<<<<<<<<
//<<<<<<<<<< all of the loop to create the CSV
for (Long l: idList) {
if (afterFirst) {
idListAsCSV.append(",");
}
afterFirst = true;
idListAsCSV.append(String.valueOf(l));
}
StringBuilder sb = new StringBuilder("SELECT * FROM ").append(DBHelper.TableX.NAME);
sb.append(" WHERE " + DBHelper.TableX.COLUMN_ID + " IN(").append(idListAsCSV).append(") "); //<<<<<<<<<<
switch (order) {
case DBHelper.TableX.FIRSTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_DESC);
break;
case DBHelper.TableX.FIRSTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_FIRSTNAME_ASC);
break;
case DBHelper.TableX.LASTNAME_DESCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_DESC);
break;
case DBHelper.TableX.LASTNAME_ASCENDING:
sb.append(DBHelper.TableX.ORDER_BY_LASTNAME_ASC);
break;
default:
break;
}
sb.append(";");
return roomDao.rawq(new SimpleSQLiteQuery(sb.toString(),null));
}
i.e. provide a CSV (although I vaguely recall being able to pass an array)
To use bind arguments (the recommended way as binding arguments protects against SQL injection) then you need a ? for each value and a corresponding array of objects.
So for 3 id's you need IN(?,?,?) and the actual values, the bind arguments, in an Object[]. The following is an example that does this noting that it shows 2 ways of building the Object[] (the bind arguments/values):-
private List<TableXEntity> loadByidList(long[] idlist) {
List<Object> bindargs = new ArrayList<>(); // way 1
Object[] args4Bind = new Object[idlist.length]; // way 2
StringBuilder placeholders = new StringBuilder(); // for the ? placeholders
/* Build the sql before the place holders */
StringBuilder sql = new StringBuilder("SELECT * FROM ")
.append(DBHelper.TableX.NAME)
.append(" WHERE ")
.append(DBHelper.TableX.COLUMN_ID)
.append(" IN (");
boolean afterfirst = false;
int i = 0; /* using for each so have index counter (as opposed to for(int i=0 ....) */
for (long l: idlist) {
bindargs.add(l); // for way 1
args4Bind[i++] = String.valueOf(l); // for way 2
if (afterfirst) {
placeholders.append(",");
}
afterfirst = true;
placeholders.append("?");
}
/* finalise the SQL */
sql.append(placeholders.toString())
.append(");");
//return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),bindargs.toArray())); // way 1
return roomDao.rawq(new SimpleSQLiteQuery(sql.toString(),args4Bind)); // way 2
}
Please try this, here it has working!
Try this simple trick to pass the arguments for IN operator-
List<Object> argList = new ArrayList<>();
argList.add("3");
argList.add("6");
Then prepare your raw query string:
Note- Match your argument list size with '?' size
String selectQuery = "SELECT * FROM task WHERE id IN (?,?)";
After this pass the raw query string to SimpleSQLiteQuery-
SimpleSQLiteQuery rawQuery = new SimpleSQLiteQuery(selectQuery, args.toArray());
Then fetch the List using DAO:
List<UserTable> taskList1=DatabaseClient
.getInstance(getApplicationContext())
.getAppDatabase()
.userTableDAO()
.getAllList(query);
We can do it in kotlin in the more simpler way.
Let's create two helper methos
object Helper {
fun sqlIn(list: List<Any>, bindArgs: MutableList<Any>): String {
bindArgs.apply { this.addAll(list) }
return "IN (${list.joinToString(",") { "?" }})"
}
fun sqlNotIn(list: List<Any>, bindArgs: MutableList<Any>): String = "NOT ${sqlIn(list, bindArgs)}"
}
Then you can use it in anywhere else
val ids = listOf(1, 2, 3)
val ownerId = 10
val bindArgs = mutableListOf<Any>()
val query = "SELECT * FROM posts WHERE id ${Helper.sqlIn(ids, bindArgs)} AND owner_id = ?"
bindArgs.add(ownerId)
dao.query(
SimpleSQLiteQuery(query, bindArgs.toTypedArray())
)
Here is my code:
public static Map<String, List<Customer>> readCustomerData() throws IOException {
Map<String, List<Customer>> customers =
Files.lines(Paths.get("customer.csv"))
.map(line -> line.split("\\s*,\\s*"))
.map(field -> new Customer(
Integer.parseInt(field[0]), field[1],
Integer.parseInt(field[2]), field[3]))
.collect(Collectors
.groupingBy(Customer::getName));
System.out.println (customers);
return customers;
}
I notice that this code read my data in the csv file into one element like this:
(Ali = ["1 Ali 1201345673 Normal"] , Siti = ["2 Siti 1307891435 Normal"])
But in my thinking , I would like to read the data like the array list such as for Ali: 1 is an element , Ali is an element , 1201345673 is an element and Normal is another element in the list in the Map customer. How can I modify my code to do such a thing?
This is my Customer class just in case:
public class Customer {
private int customerNo;
private String name;
private int phoneNo;
private String status;
public Customer () {}
public Customer (int customerNo, String name, int phoneNo, String status){
this.customerNo = customerNo;
this.name = name;
this.phoneNo = phoneNo;
this.status = status;
}
public String getName(){
return name;
}
public String toString(){
return customerNo + " " + name + " " + phoneNo + " " + status;
}
Here is my csv file:
1,Ali,1201345673,Normal
2,Siti,1307891435,Normal
Thank you for your attention.
Assuming that the customer names are unique, there's no need to return a Map<String, List<Customer>>, since each List will contain a single Customer.
You can change your code to:
Map<String, Customer> customers =
Files.lines(Paths.get("customer.csv"))
.map(line -> line.split("\\s*,\\s*"))
.map(field -> new Customer(
Integer.parseInt(field[0]), field[1],
Integer.parseInt(field[2]), field[3]))
.collect(Collectors.toMap(Customer::getName, Function.identity()));
And if the names are not unique, you can index the customers by the customer IDs.
As for I would like to read the data like the array list such as for Ali: 1 is an element , Ali is an element , 1201345673 is an element and Normal is another element in the list in the Map customer - this doesn't make sense to me. You already create a Customer object from each line of your input, which is much more useful and type safe compared to a List of properties.
I've two classess CsvRead and MyOwnClass.
In CsvRead I've a method public static List getDataFromCsv(); It returns list of all data. And this data I want to take in another method in class MyOwnClass and return there as list of objects of My OwnClass
It looks like this:
List<String> dataFromCsv = new ArrayList<String>();
And in another class, I want to convert it to List<Object> of my class.
private static List<String> getDataFromCsvClass = new ArrayList<String>();
getDataFromCsvClass = CsvReader.getAllCsvData(filename);
String name = dataFromCsv[0];
String surname = dataFromCsv[1];
String birth = dataFromCsv[2];
I want to return new MyOwnClass(name, surname, birth);
MY ERROR: array required but List found: String name = allData[0]; etc
You can create a method to convert a String to MyOwnClass and use stream to map the elements, e.g.:
public static MyOwnClass convertToObject(String element){
String[] tokens = element.split(",");
return new MyOwnClass(tokens[0], tokens[1], tokens[2]);
}
//code to convert
List<String> dataFromCsv = new ArrayList<String>();
List<MyOwnClass> list = dataFromCsv.stream()
.map(e -> convertToObject(e))
.collect(Collectors.toList());
However, this may not work if let's say name or surname contains comma. In which case, I would recommend having a look at OpenCSV library and this example of how to read csv into objects.
Supposing that the list contains the name, surname and birth in every group of 3 strings (i.e., the elements on index 0, 3, 6, 9 etc. contain the name), you might try the following:
public List<MyOwnClass> convertCsvData(List<String> csv_data)
{
// Initialize result
List<MyOwnClass> result;
result = new ArrayList<MyOwnClass>();
// Parse data
int counter;
String name;
String surname;
String birth;
for (counter = 0; counter < csv_data.size(); counter += 3)
{
name = csv_data.get(counter);
surname = csv_data.get(counter + 1);
birth = csv_data.get(counter + 2);
result.add(new MyOwnClass(name, surname, birth));
}
// Done
return (result);
} // convertCsvData
Somthing like this :
private static MyOwnClass toMyOwnClass(String str){
String[] object= str.split(",");
return new MyOwnClass(object[0], object[1], object[2]);
}
List<String> dataFromCsv = new ArrayList<String>();
List<MyOwnClass> list = new ArrayList<>();
for(String string : dataFromCsv ){
if(StringUtils.isNoneEmpty(string)){
list.add(toMyOwnClass(string));
}
}
And then you return your list
As the question reads.... and I do NOT want to use multiple maps, just one map.
My goal is to get a list of the names I enter in the input.
I have tried like a hundred different for-loops, but I always tend to end up with a list of the whole map and/or that the duplicate key is overridden.
import java.util.*;
public class Another {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
String name;
HashMap<String, ToA>wordkey = new HashMap<String, ToA>();
ToA a = new ToA("Doolin", "Bill", "18580824-1464");
ToA b = new ToA("Dalton", "Bob", "18701005-2232");
ToA c = new ToA("James", "Jesse", "18470905-2401");
ToA d = new ToA("Dalton", "Emmet", "18710713-0818");
wordkey.put("Doolin", a);
wordkey.put("Dalton", b);
wordkey.put("James", c);
wordkey.put("Dalton", d);
System.out.println("Efternamn:");
name = scan.next();
}
}
public class ToA{
private String fname, lname, dob;
public ToA(String fname, String lname, String dob){
this.fname = fname;
this.lname = lname;
this.dob = dob;
}
public String getFname(){
return fname;
}
public String getLname(){
return lname;
}
public String getDob(){
return dob;
}
public String toString(){
return "\nFirstname: " + fname + "\nSurname: " + lname + "\nDateOfBirth: " + dob;
}
}
For inputting Dalton, I would like the output
Firstname: Bill
Surname: Dalton
DateOfBirth: 18701005-2232
Firstname: Emmet
Surname: Dalton
DateOfBirth: 18710713-0818
I'm really stuck with this so any help is highly appreciated, Thanks
To post my comment as an answer: use a Map<String, List<ToA>> like this:
Map<String, List<ToA>> wordkey = new HashMap<>();
ToA a = new ToA("Doolin", "Bill", "18580824-1464");
ToA b = new ToA("Dalton", "Bob", "18701005-2232");
ToA c = new ToA("James", "Jesse", "18470905-2401");
ToA d = new ToA("Dalton", "Emmet", "18710713-0818");
wordkey.put("Doolin", Arrays.asList(a));
wordkey.put("James", Arrays.asList(c));
wordkey.put("Dalton", Arrays.asList(b, d));
To print the names based on the input, you can do something like this:
System.out.println("Efternamn:");
name = scan.next();
List<ToA> toas = wordkey.get(name);
if (toas != null) {
System.out.println("ToAs");
for (ToA toa : toas) {
System.out.println("ToA: " + toa);
}
}
else {
System.out.println("No ToAs found for input: " + name);
}
There are several possibilities for what you are trying to achieve. A simple one would be to use Guavas Multimap or to use Apaches MultiMap.
Another possibility is to "wrap" the Map in a class and keep a List<ToA> as Value of the Map. You'd override the put, remove and get methods to what you need
//This is my controller
#RequestMapping("/getList")
public ModelAndView getList(#RequestParam(value="page",required=false) Integer page,Model model,#RequestParam(value="page_size",required=false) Integer page_size){
page=0;
page_size=4;
List<Employee> empList = empService.getEmpList1(page,page_size);
Employee emp=new Employee();
model.addAttribute("employee", emp);
return new ModelAndView("empList", "empList", empList);
}
//This is my Sql Query........
select * from EmpDetails LIMIT +page+","+page_size;
//This is my jsp.......for next button.....
Next:
By using this code I am getting the records(0-4) of employee in one page.
My question is when I click on next button the records(4-8) of employees must be displayed in other page. Can you please tell me how to write the code?
Try this code
#RequestMapping("/getList")
public String getList(#RequestParam(value = "page", required = false) Integer page, Model model,
#RequestParam(value = "page_size", required = false) Integer page_size) {
if (page == null) {
page = 0;
}
if (page_size == null) {
page_size = 4;
}
List<Employee> empList = empService.getEmpList1(page, page_size);
Employee emp = new Employee();
model.addAttribute("employee", emp);
model.addAttribute("empList", empList);
model.addAttribute("page", page);
model.addAttribute("page_size", page_size);
return "empList";
}
//This for next button in jsp..... Try and let me know if this link work?
Next:
//This is my Sql Query........
recordStart = page * page_size;
select * from EmpDetails LIMIT +recordStart+","+page_size;
You should not assign strict values to page and page_size variables in your method. Instead of this you should get values from parameters passed to method.
#RequestMapping("/getList")
public ModelAndView getList(#RequestParam(value="page",required=false) Integer page,Model model,#RequestParam(value="page_size",required=false) Integer page_size){
List<Employee> empList = empService.getEmpList1(page,page_size);
Employee emp=new Employee();
model.addAttribute("employee", emp);
return new ModelAndView("empList", "empList", empList);
}
Of course if your page and page_size is optional then you have to check if values are passed. If not then you have to assign some default values.