Sorting a Wicket DataTable by a PropertyColumn that contains some null values - sorting

Here is some psuedo code for my table:
List<IColumn<Foo>> columns = new ArrayList<IColumn<Foo>>();
columns.add(new PropertyColumn<Foo>(Model.of("Name"), "name", "name"));
columns.add(new PropertyColumn<Foo>(Model.of("Leader"), "leader.lastName",
leader.fullName"));
fooTable = new AjaxFallbackDefaultDataTable<Foo>("fooTable", columns,
provider, 10);
The class Foo contains among other fields:
private String name;
private User leader;
And class User contains Strings userName, password, firstName, lastName, email, along with:
public String getFullName() {
// Use blank strings in place of "null" literals
String f = firstName != null ? firstName: "";
String l = lastName != null ? lastName: "";
return (l + ", " + f);
}
Every Foo has a name, and every User has has a first and last name. However, not every Foo has a leader, so in the table above, there are several null/ blank values for leader. When the table is sorted by leader by clicking the column header, all Foo's without a leader are missing. When sorted again by name, they return, with blanks for leader.
Is there anyway to fix this? A preference where nulls always sort last would be best, but I'll take anything where they don't just disappear.

The correct sorting is a responsibility of the SortableDataProvider. If it is making items disappear, you should revise it's logic.

Related

How to update room database and How to get the insert status is working or completed?

The first:
I've got dataList from retrofit And insert Room Database.
I want to change dataList(Like insert a element). My Room Database can work because I used OnConflictStrategy.REPLACE. but when I delete dataList some elements, My Room Database can not delete elements.
Dao:
#Insert (onConflict = OnConflictStrategy.REPLACE)
suspend fun insertData(dataList : List<Data>)
Entity:
#Entity
data class Data(
#PrimaryKey val Id : Long,
val Fl : String,
val FlMc : String,
val Dm : String,
val Mc : String,)
ViewModel:
fun insertData(dataList: List<Data>) = viewModelScope.launch {
dataRepository.insertData(dataList)
}
//get data from server
fun getData():LiveData<List<Data>>
Activity:
dataViewModel.getData().observer(this){
dataViewModel.insertData(it)
}
How to resolve this situation except DELETE ALL THEN INSERT
The second:
I want to use a progressbar to indicate that I am inserting dataList
How to get the insert status is working or completed
If I understand correctly, you issue is that you cannot delete because you are building a DataList item but don't know the primary key value as it's generated.
As you haven't shown the DataList entity then assuming it is like:-
#Entity
data class DataList(
#PrimaryKey(autogenerate = true)
val id: Long,
val othercolumns: String
....
)
and if you change from suspend fun insertData(dataList : List<Data>) to suspend fun insertData(dataList : List<Data>): List<Long> (i.e. added the List as the result)
Then you have the values of the id column in the result. In the case above the value is the value of the id column.
If the #PrimaryKey is not an integer type e.g. a String then the long returned WILL NOT be the value of the primary key. It will be a special value known as the rowid.
In short using an integer with primary key makes the column an alias of the rowid. if not an integer primary key then it is not an alias BUT the rowid still exists.
You can still use the rowid to access a specific row as the rowid MUST be a unique value. e.g. (again assuming the above) you could have an #Query such as
#Query("SELECT * FROM the_datalist_table WHERE rowid=:rowid")
suspend fun getDataListById(rowid: Long)
Only of use if you know the rowid though.
You could get rowid's say by using
#Query("SELECT rowid FROM the_datalist_table WHERE othercolumns LIKE :something")
suspend fun getRowidOfSomeDataLists(something: String): List<Long>
still not of great use as the selection criteria would also be able to provide a list of Datalists.
Additional re the comment:-
How to use in viewModel or Activity?
As an example you could do something like :-
fun insertData(dataList: List<Data>) = viewModelScope.launch {
val insertedDataList: ArrayList<Data> = ArrayList()
val insertedIdList = dataRepository.insertData(dataList)
val notInsertedDataList: ArrayList<Data> = ArrayList()
for(i in 0..insertedIdList.size) {
if (insertedIdList[i] > 0) {
insertedDataList.add(
Data(
insertedIdList[i], //<<<<< sets the id as per the returned list of id's
dataList[i].Fl,
dataList[i].FlMc,
dataList[i].Dm,
dataList[i].Mc)
)
} else {
notInsertedDataList.add(
Data(
insertedIdList[i], //<<<<< sets the id as per the returned list of id's WILL BE -1 as not inserted
dataList[i].Fl,
dataList[i].FlMc,
dataList[i].Dm,
dataList[i].Mc
)
)
}
}
val notInsertedCount = notInsertedDataList.size
val insertedCount = insertedDataList.size
}
So you have :-
insertedDataList an ArrayList of the successfully inserted Data's (id was not -1) with the id set accordingly.
notInsertedDataList an ArrayList of the Data's that were not inserted (id was -1) id will be set to -1.
insertedCount an Int with the number inserted successfully.
notInsertedCount and Int with the number not inserted correctly.
DELETE ALL
To delete all rows, unless you extract all rows you can't use the convenience #Delete, as this works on being provided the Object (Data) and selecting the row to delete according to the primary key (id column).
The convenience methods #Delete, #Update, #Insert are written to generate the underlying SQL statement(s) bases upon the object (Entity) passed.
e.g. #Delete(data: Data) would generate the SQL DELETE FROM data WHERE id=?, where ? would be the value of the id field when actually run.
The simpler way to delete all columns is to use the #Query annotation (which handles SQL statements other than SELECT statements). So you could have.
#Query("DELETE FROM data")
fun deleteAllData()
note that this does not the return the number of rows that have been deleted.

Room database: how to retrieve column names into list of strings?

I have a Room table named "addresses" with 15 columns. I retrieve one row and want to get values into List< String >, not List< Addresses >. Is that possible?
#Query("SELECT * FROM addresses WHERE myid= :id")
List<String> getAddressAsList(int id);
Moreover, is it possible to retrieve database table column names together with values into list of map <"column name","value"> like this?
#Query("SELECT * FROM addresses WHERE myid= :id")
List<Map<String, String> getAddressAsList(int id);
You can use a SupportSQLiteDatabase but not a Dao.
Say you have, in an activity,
db = yourRoomDatabase.getInstance(this);
i.e. you'd typically then use something like yourDao = db.getYourDao(); and then
myAddressList = dao.getAddressList();
You can then do:-
SupportSQLiteDatabase sdb = db.getOpenHelper().getWritableDatabase();
Cursor csr = sdb.query("SELECT * FROM address WHERE myid=?",new String[]{String.value(id)}
Then to get the list of column names in the Cursor you can use:-
val columnNames = csr.columnNames
To get the Map of columnnames and values you could use :-
val columnsAndValues: MutableMap<String,String> = mutableMapOf()
while (csr.moveToNext()) {
for (i: Int in 0..csr.columnCount) {
columnsAndValues.put(csr.columnNames.get(i),csr.getString(i))
}
}
csr.close()

Conversion failed when converting the varchar value 'Zon7' to data type int

I'm getting this error :
Conversion failed when converting the varchar value 'Zon7' to data
type int.
establishments = GetEstablishments(waters.Select(t => ReplaceZonToEmptyString(t.IdGeographie)));
public static int ReplaceZonToEmptyString(string zoneId)
{
zoneId.Replace("Zon", string.Empty);
var sbInput = zoneId.Replace(" ", string.Empty);
return Convert.ToInt32(sbInput.ToString());
}
public static IQueryable<Etablissement> GetEstablishments(IQueryable<int> ids)
{
return from zone in entities.Zones
where ids.Contains(zone.IdZone)
select zone.IdBatimentNavigation.IdEtablissementNavigation;
}
var result = establishments.ToList();
in database i have a column of type varchar the column name is 'IdGeographie' with values that start with 'Zon', something like this "ZonXXXX"
You are trying to compare a VARCHAR column with values of type int. One of these values will have to change and since it con not be the SQL column, it has to be the compare value:
public static string ReplaceZonToEmptyString(string zoneId)
{
var sbInput = new StringBuilder(zoneId);
sbInput.Replace("Zon", string.Empty);
sbInput.Replace(" ", string.Empty);
return sbInput.ToString();
}
public static IQueryable<Etablissement> GetEstablishments(IQueryable<string> ids)
{
return from zone in entities.Zones
where ids.Contains(zone.IdZone)
select zone.IdBatimentNavigation.IdEtablissementNavigation;
}
If the signature of the methods can't change, you have to do the conversion within GetEstablishments:
public static IQueryable<Etablissement> GetEstablishments(IQueryable<int> ids)
{
var textIds = ids.Select(id => id.ToString());
return from zone in entities.Zones
where textIds.Contains(zone.IdZone)
select zone.IdBatimentNavigation.IdEtablissementNavigation;
}
Note that in waters.Select(t => ReplaceZonToEmptyString(t.IdGeographie)), the value waters must be a materialized list of values (i.e. not another EF query), since your replace operation can not work within Entity Framework (in either of the options).
What do you think the following code will do:
string original = "Hello World!";
string changed = original.Recplace("o", "xx");
original won't be changed, changed will equal "Hellxx, Wxxrld".
So you should change your ReplaceZonToEmptyString:
public static int ExtractZoneNr(string zoneId)
{
string idWithoutZon = zoneId.Replace("Zon", string.Empty);
string sbInput = idWithoutZon.Replace(" ", string.Empty);
return Int32.Parse(sbInput);
}
Alas this will only work on IEnumerable, not on IQueryable
A better solution:
This solution only works if IdGeographie is the three letters "Zon" followed by the string representation of the IdZone and nothing else. So no spaces, no leading zeroes etc: "Zon4" and not "Zon004", nor "Zon 4"
You have two IQueryables one for waters and one for zones:
IQuerybale<Water> waters = ... // probably entities.Waters
IQueryable<Zone> zones = entities.Zones
Every Zone contains an int property IdZone, and a property .IdBatimentNavigation.IdEtablissementNavigation, which seems to be of type Etablissement
Furhermore every Water has a string property GeographieId in the format "ZonX" where X is an integer number.
Now you want to query the IdBatimentNavigation.IdEtablissementNavigation of all Zones with an IdZone that equals the X part of one or more of the Waters
For example: if you have the following Waters
[0] GeographieId = "Zon10"
[1] GeographieId = "Zon42"
[2] GeographieId = "Zon7"
And you have Zones with IdZone: 4, 42, 30, 7, 22.
Then as a result you want the IdBatimentNavigation.IdEtablissementNavigation of the Zones with IdZone: 42 any 7 (in any order)
Why not join waters and zones?
var result = zones.
.Select(zone => new
{
GeographieId = "Zon" + zone.IdZone.ToString(),
Etablissement = zoneIdBatimentNavigation.IdEtablissementNavigation,
})
.Join(waters, // join with waters
zone => zone.GeographieId, // from zone take the GeoGraphieId
water => water, // from waters take idGeographie
(zone, water) => zone.Etablissement); // when thay match return
A better solution would be to try to remove the "Zon" part from IdGeographie, and Parse the remaining XXXX to an int. Alas there is no function that can perform the parsing AsQueryable.

Getting a column by string name

I'm trying to update a record given the customer Id, the row Id, and a dynamic column name.
Thus far I have the following, with the trouble spot marked by ***:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id && x.RVals.Id == rId && x.***column?*** == column).First();
entry = value;
}
I haven't been able to find a good example of how to do this.
Addition after comments at the end
The reason you couldn't find examples is because it is not a good design.
Your method is very error prone, difficult to test and horrible to maintain. What if someone types the incorrect column name? What if you try to assign a string to the customer's birthday? And even if you would implement some string checking for column names and proposed values, then your program wouldn't work anymore after someone changes the names or the types of the columns.
So let's redesign!
Apparently you have a Customer with an Id and a property Rvals. This property Rvals also has a property Id.
You also have a function GetRValId that can convert a string rval to an int rvalId.
What you want, is given an Id and a string rval, you want to update one of the columns of the first Customer with this Idand rValId.
Side questions: Can there be more than one Customer with Id? In that case: are you sure Id is an ID? What do you want if there are more matching Customers? Update all customers or update only the first one? Which customer do you define as the first customer?
Leaving the side questions aside. We want a function signature that reports errors at compile time if you use non-existing customer properties, or if you try to assign a string to a Birthday. Something like this perhaps?
Update the name of the customer:
int customerId = ...
string rval = ...
string proposedName = "John Doe";
UpdateCustomerRecord(id, rval, customer => customer.Name = proposedName);
Update the Birthday of the customer:
DateTime proposedBirthday = ...
UpdateCustomerRecord(id, rval, customer => customer.Birthday = proposedBirthday)
This way you can't use any column that does not exist, and you can't assign a string to a DateTime.
You want to change two values in one call? Go ahead:
UpdateCustomerRecord(id, rval, customer =>
{
customer.Name = ...;
customer.Birthday = ...;
});
Convinced? Let's write the function:
public void UpdateCustomerRecord(int customerId, string rval, Action<Customer> action)
{
// the beginning is as in your function:
var rId = GetRvalId(rval);
// get the customer that you want to update:
using (var _Context = ...)
{
// get the customer you want to update:
var customerToUpdate = _Context.Customers
.Where(customer => customer.Id == Id
&& customer.RVals.Id == rId)
.FirstOrDefault();
// TODO: exception if there is no customerToUpdate
// perform the action and save the changes
action(customerToUpdate);
_context.SaveChanges();
}
Simple comme bonjour!
Addition after comments
So what does this function do? As long as you don't call it, it does nothing. But when you call it, it fetches a customer, performs the Action on the Customer you provided in the call, and finally calls SaveChanges.
It doesn't do this with every Customer, no it does this only with the Customer with Id equal to the provided Id and customer.RVals.Id == ... (are you still certain there is more than one customer with this Id? If there is only one, why check for RVals.Id?)
So the caller not only has to provide the Id, and the RVal, which define the Customer to update, but he also has to define what must be done with this customer.
This definition takes the form of:
customer =>
{
customer.Name = X;
customer.BirthDay = Y;
}
Well if you want, you can use other identifiers than customer, but it means the same:
x => {x.Name = X; x.BirthDay = Y;}
Because you put it on the place of the Action parameter in the call to UpdateCustomerRecord, I know that x is of type Customer.
The Acton statement means: given a customer that must be updated, what must we do with the customer? You can read it as if it was a Function:
void Action(Customer customer)
{
customer.Name = ...
customer.BirthDay = ...
}
In the end it will do something like:
Customer customerToUpdate = ...
customerToUpdate.Name = X;
customerToUpdate.BirthDay = Y;
SaveChanges();
So in the third parameter, called Action you can type anything you want, even call functions that have nothing to do with Customers (probably not wise). You have an input parameter of which you are certain that it is a Customer.
See my earlier examples of calling UpdateCustomerRecord, one final example:
UpdateCustomerRecord( GetCustomerId(), GetCustomerRVal,
// 3rd parameter: the actions to perform once we got the customerToUpdate:
customer =>
{
DateTime minDate = GetEarliestBirthDay();
if (customer.BirthDay < minDate)
{ // this Customer is old
customer.DoThingsThatOldPeopleDo();
}
else
{ // this Customer is young
customer.DoThingsThatYoungPeopleDo();
}
}
}
So the Action parameter is just a simpler way to say: "once you've got the Customer that must be updated, please perform this function with the Customer
So if you only want to update a given property of the customer write something like:
UpdateCustomerRecord(... , customer =>
{
Customer.PropertyThatMustBeUpdated = NewValueOfProperty;
}
Of course this only works if you know which property must be updated. But since you wrote "I am trying to update a specific cell." I assume you know which property the cells in this column represent.
It is not possible to pass the column name as the string value in LINQ. Alternate way to do it, if you have the limited number of the column name which can be passed then it can be achieved as below:
public void UpdateRecord(int Id, string rval, string column, string value)
{
var rId = GetRvalId(rval);
var entry = _context.Customers
.Where(x => x.Id == Id &&
x.RVals.Id == rId &&
(x.column1 == value || column == column1) &&
(x.column2 == value || column == column2) &&
(x.column3 == value || column == column3) &&
(x.column4 == value || column == column4) &&
(x.column5 == value || column == column5) &&
)).First();
entry = value;
}
UpdateRecord(5, "rval", "column1", "value");
UpdateRecord(5, "rval", "column2", "value");
UpdateRecord(5, "rval", "column3", "value");
Here, suppose you have the 5 columns that can be passed while calling the funcion UpdateRecord then you can add the 5 clauses in the WHERE as above.
Other way to do it dynamic LINQ
var entry = db.Customers.Where(column + " = " + value).Select(...);

linq query how to take out spaces in strings

I am trying to match a string typed in by a user to a string in a database.
Before I try match them i need to take the spaces out both of them.
how do i do this?
public static Product GetProductbypart(ModelContainer context, string partnumber)
{
var query = from product in context.Products
where product.Partnumber == partnumber
select product;
return query.FirstOrDefault();
}
this is my query which works if the user types in the exact part number. But some users may type it in with too many spaces or too less.
I want to take the partnumber take out the spaces. Then take the product.Partnumber and take of the spaces of that also, to see if there is a match.
Sample inputs:
MC-9a 1a AC24V
MC-9a 50/60Hz
1
123
MC+123-1
F6h67e
_8jj+j7s
string partnumber = partnumber.Replace(" ", String.Emtpy);
var query = from product in context.Products
where product.Partnumber.Replace("", String.Empty) == partnumber
select product;
This removes spaces in the strings product.Partnumber and partnumber. However, if you use linq-to-SQL the part product.Partnumber.Replace(..) won't work. But I'm not sure why you have to remove spaces of the product number in the database. Sounds like inconsistent data to me.
public static Product GetProductbypart(ModelContainer context, string partnumber)
{
partnumber = partnumber.Replace(" ", String.Empty);
var products = from product in context.Products
select product;
foreach(var item in products)
{
if (item.Partnumber != null)
{
item.Partnumber = item.Partnumber.Replace(" ", String.Empty);
if (item.Partnumber == partnumber)
{
var query = from product in context.Products
where product.Id == item.Id
select product;
return query.FirstOrDefault();
}
}
}
return null;
}
This is how i did it
In this case I would think about implementing it a little bit differently. If you need your table only in order to compare it with the user input, you can remove spaces in the database in advance, before running the query. Also there is no point in removing spaces in database, you can do it in client code.
In this case you will be able to use regular Equals and also it should be a little bit more efficient.
I think the best answer here is to not try and figure out how the user entered the data, instead make sure that they entered a valid part number in the first place. Can you provide some form of input format? Even if it means using hyphens where spaces are, you can always remove them before you check, but at least you'll have data in the correct format.

Resources