I want to order my posts in the FirebaseRecyclerView by the String Votes. It contains a number as a String. If I write orderbychild("Votes") it shows up the smallest number first.
How can I turn that around? Cheers!
You don't necessarily turn it around.
Let's say your data looks like this:
{
"polls": {
"id1": { "name": "lorem", "votes": 4 }
"id2": { "name": "ipsum", "votes": 3 }
"id3": { "name": "dolor", "votes": 5 }
"id4": { "name": "sit", "votes": 2 }
"id5": { "name": "amot", "votes": 6 }
}
}
If for instance you need the top 4 you would do this:
.orderByChild("Votes").limitToFirst(4)
Would return:
{
"id4": { "name": "sit", "votes": 2 }
"id2": { "name": "ipsum", "votes": 3 }
"id1": { "name": "lorem", "votes": 4 }
"id3": { "name": "dolor", "votes": 5 }
}
If you want the other end, you would do this instead:
.orderByChild("Votes").limitToLast(4)
And the return would be:
{
"id2": { "name": "ipsum", "votes": 3 }
"id1": { "name": "lorem", "votes": 4 }
"id3": { "name": "dolor", "votes": 5 }
"id5": { "name": "amot", "votes": 6 }
}
If you need all of them, just order in the ui.
A quick and rather flexible way to do this on server side is to add an extra field and index your data both with Votes and negativeVotes, where negativeVotes=-Votes
Then for reverse order you do this:
orderbychild("negativeVotes")
And for regular order you do this:
orderbychild("Votes")
A way that I think better is ordering in client-side using reverse().
mLayoutManager = new LinearLayoutManager(getActivity());
mLayoutManager.setReverseLayout(true);
mLayoutManager.setStackFromEnd(true);
the code below is how I fixed the issue. it uses an 3 integers - a max value the max value is equal to what ever you want with in the bounds of the datatype restrictions.
int round = 5;
int roundKills = 1000;
int points = 500;
round = round-99999;
roundKills = roundKills-999999;
points = points-99999999;
String oundkills = Integer.toString(roundKills);
String oints = Integer.toString(points);
String ound = Integer.toString(round);
LeaderBoardInput leaderBoardInput = new
LeaderBoardInput(user,"SM",oundkills,oints,ound);
the LeaderboardInput class is just getters and setters for values that are being passed to the database
reference.child(user+System.currentTimeMillis()).setValue(leaderBoardInput);
when you pull the value, you would do something like the following:
round = round+99999;
roundKills = roundKills+999999;
points = points+99999999;
for your example if you have 5 votes and a max vote of 9,999,999
the code would look like this
int votes = 5;
long invertVotes = votes - 9999999;
String dbVotes = Integer.toString(invertVotes);
rootNode = FirebaseDatabase.getInstance();
reference = rootNode.getReference("scores");
reference.child("Votes"+System.currentTimeMillis()).setValue(dbVotes);
Query reference = FirebaseDatabase.getInstance()
.getReference().child("Votes").orderByChild("Votes");
reference.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
//parse data to recycler view adapter and call //notifyDatasetChange()
Iterable<DataSnapshot> children = dataSnapshot.getChildren();
for (DataSnapshot child : children) {
Votes voteValues = child.getValue(voteinput.class);
int value = votevalues.getvotes()+9999999;
scores.add(new Votes( value));
}
getdb = false;
}
});
with Votes be a getter setter class and the scores variable being a list of the Votes class instances
this will order votes 9999994 in db but print 5 to list
out put of multiple values will 5 4 3 2 1 instead of 1 2 3 4 5
I would like to add just one more thing to #Ari approch.
private Query getQuery() {
Query query;
if(last_value == -1) {
query = getPostViewCountRef()
.orderByChild("negativeViewsCount")
.limitToFirst(ITEMS_PER_PAGE);
}else {
query = getPostViewCountRef()
.orderByChild("negativeViewsCount")
.startAfter(last_value)
.limitToFirst(ITEMS_PER_PAGE);
}
return query;
}
as you can see we limitToFist instead of limitTolast
Related
I'm having trouble based on the number of digits in string listing ..
I want the field named "score" to be sorted from higher to lower, but for example, there are 10 users, 9 of them have score with 5 digits, while 1 user has score with 6 digits, so I want the higher score to be on top of the list which is with 6 digits as I mentioned above but once the score becames 6 digits it goes at the end of the list which should be on top..
How can I solve this problem .. ??
Query query = ref.child("Kullanıcılar").orderByChild("puan");
query.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
list.clear();
for (DataSnapshot ds: dataSnapshot.getChildren())
{
i++;
user = ds.getValue(User.class);
list.add(i +" - "+ user.getIsim().toString() + " " + user.getPuan().toString());
//list.add(user.getIsim().toString() + " " + user.getPuan().toString());
}
listView.setAdapter(adapter);
Collections.reverse(list);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
JSON:
{
"Kullanıcılar": {
"5bdhzNz3SlhzcCAYJaDpJyeppSx2": {
"email": "egemenn1453#gmail.com",
"id": "5bdhzNz3SlhzcCAYJaDpJyeppSx2"
"isim": "egemen"
"puan": "24000"
},
"7iJJxUswPPhkLXfm6LNHNCCRSeJ3": {
"email": "kalantolga#hotmail.com",
"id": "7iJJxUswPPhkLXfm6LNHNCCRSeJ3",
"isim": "falanfilan",
,
"puan": "25000"
},
"JoSwIRjzrxTHTz7CXaiQLAjQMCZ2": {
"email": "terketyaman160#gmail.com",
"id": "JoSwIRjzrxTHTz7CXaiQLAjQMCZ2"
"isim": "fropsello",
"puan": "56525"
}
}
Since you're storing the scores as strings, Firebase will sort them on their lexicographical (string) value. In lexicographical order "9" is after "10", because "9" > "1". This may seem illogical at first, but is completely correct.
The recommended solution is to store your scores as numbers:
{
"Kullanıcılar": {
"5bdhzNz3SlhzcCAYJaDpJyeppSx2": {
"email": "egemenn1453#gmail.com",
"id": "5bdhzNz3SlhzcCAYJaDpJyeppSx2"
"isim": "egemen"
"puan": 24000
},
"7iJJxUswPPhkLXfm6LNHNCCRSeJ3": {
"email": "kalantolga#hotmail.com",
"id": "7iJJxUswPPhkLXfm6LNHNCCRSeJ3",
"isim": "falanfilan",
"puan": 25000
},
"JoSwIRjzrxTHTz7CXaiQLAjQMCZ2": {
"email": "terketyaman160#gmail.com",
"id": "JoSwIRjzrxTHTz7CXaiQLAjQMCZ2"
"isim": "fropsello",
"puan": 56525
}
}
Now the nodes are numeric and will be correctly sorted, since 10 > 9 in numeric sort order.
"PaymentMode": "Cash",
"Amount": 1000,
"PaymentReferenceNumber": "",
},
{
"PaymentMode": "CcAve",
"Amount": 500,
"PaymentReferenceNumber": "",
},
{
"PaymentMode": "cash",
"Amount": 0,
"PaymentReferenceNumber": "IN0001-113",
},
{
"PaymentMode": "Credited",
"Amount": 500,
"PaymentReferenceNumber": "IN0001-114",
}
This is how i get my json response. What i need to do is that, those 'amount' value whose corresponding payment reference number is empty or null needs to be added.This is what i have done
public int getTotalPayments() {
int total = 0;
for(OrderPayment payment : orderPayments) {
if(payment.getPaymentReferencenumber().isEmpty()||payment.getPaymentReferencenumber().equals("NULL")){
total += payment.getAmount();
}
}
return total;
}
But I am getting wrong. Can anyone help
Look at your logic, you say you're getting the sum of:
References that are empty
OR
References that are NOT "NULL"
And most of the stuff is not null.
To answer your question, just remove the !:
if(payment.getPaymentReferencenumber().isEmpty()||payment.getPaymentReferencenumber().equals("NULL")){
Which is
- References that are empty
OR
- References that are "NULL"
Your Condition is wrong . In am assuming you are checking for a blank Reffrence number here.
So it should be as :-
public int getTotalPayments() {
int total = 0;
for(OrderPayment payment : orderPayments) {
if (isEmpty(payment.getPaymentReferencenumber())) {
total += payment.getAmount();
}
}
return total;
}
private boolean isEmpty(String str) {
if (TextUtils.isEmpty(str))
return true;
return str.toLowerCase().equals("null");
}
I have a range slider for prices of food , and based on the min and max of the slider , I want to display the foods which are in this range.
Slider code
multiSlider.setOnThumbValueChangeListener(new MultiSlider.SimpleChangeListener() {
#Override
public void onValueChanged(MultiSlider multiSlider, MultiSlider.Thumb thumb, int thumbIndex, int value) {
if (thumbIndex == 0) {
min = String.valueOf(value);
min1.setText(min);
} else {
max = String.valueOf(value);
max1.setText(max);
}
}
});
alertD.setPositiveButton("Done", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialogInterface, int i) {
SearchPrice(min, max);
}
});
Query Code
private void SearchPrice(String min, String max) {
Query searchByName = foodList.orderByChild("price").startAt(min).endAt(max);
FirebaseRecyclerOptions<Food> foodOptions = new FirebaseRecyclerOptions.Builder<Food>().setQuery(searchByName, Food.class).build();
Searchadapter = new FirebaseRecyclerAdapter<Food, FoodViewHolder>(foodOptions) {
#Override
protected void onBindViewHolder(#NonNull FoodViewHolder viewHolder, int position, #NonNull Food model) {
viewHolder.food_name.setText(model.getName());
viewHolder.food_price.setText(model.getPrice());
Structure
I tried using the startAt and endAt , but its displaying food with prices outside of the range.Any help on this please ? Is there another way of doing this type of query?
You're storing numeric values as strings in the database. Firebase sorts strings lexicographically. In lexicographical order of you have number from 1 to 12, they are sorted as "1", "10", "11", "12", "2", "3", "4", "5", "6", "7", "8", "9". So the range from "1" to "10" is precisely those two "numbers".
To solve this problem, store your numeric values as actual numbers in the database.
Also see:
firebase orderByChild returns weird order for only one child
Firebase query by date string
firebase database order by value working wrong
Firebase query ordering not working properly
other questions mentioning lexicographical sorting on Firebase
I have a json array, jsonArray, like:
[{
"phone_number": "+123456",
"name": "Bob"
},
{
"phone_number": "+234567",
"name": "Tom"
},
{
"phone_number": "+345678",
"name": "Jim"
},
{
"phone_number": "+4567890",
"name": "Jane"
},
{
"phone_number": "+5678901",
"name": "Sally"
}
]
In my onBindViewHolder I want to check if phone_number value in the json array matches username and then if there is a match then in the viewHolder.phone_user_name set the text to the corresponding value in "name" of the json array.
But all I get in my recyclerView for each cell is user : (blank)
Here's what I've tried so far:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
SharedReview r = the_Shared_reviews.get(position);
//username is in fact a phone number from the db
String username = r.getUsername();
String phone_user_name = "";
int matching = jsonArray.length();
for (int i = 0; i < matching; i++) {
try {
JSONObject object = jsonArray.getJSONObject(i);
if (object.getString("phone_number").contains(username))
{
phone_user_name = (object.getString("name"));
}
} catch (JSONException e) {
Log.e("MYAPP", "unexpected JSON exception", e);
// Do something to recover ... or kill the app.
}
}
//I want to set the text to Bob, Tom, or whatever corresponding
// phone number matches username
((ReviewHolder) viewHolder).phone_user_name.setText("user :" + phone_user_name);
}
You should create a list of Object of (Phone Number, Name) and bind it to the recycler view. in "onBindViewHolder()", you can find the corresponding object from the list which contains both fields.
I'm not sure what value is supposed to be in the SharedReview class under the username variable, but I'm assuming it's a name, not a number.
However, you are testing if your json array item named "phone_number", which in your example is an actual phone number, contains the username. Which is assumably a name.
You should replace:
if (object.getString("phone_number").contains(username))
{
phone_user_name = (object.getString("name"));
}
With:
if (object.getString("name").equals(username))
{
phone_user_name = (object.getString("name"));
}
I am using .zip operator to combine 2 API calls
What I want
I wanted to get filtered values from 2nd Observable based on some ids from 1st Observable
Eg
1st Observable returns data like (sample data)
"categories": [
{
"category": "1",
"category_name": "Wedding Venues",
"category_photo_url": "http://www.marriager.com/uploads/album/0463373001465466151-0463467001465466151.jpeg",
"category_type_id": "1",
2nd Observable returns data like :
"data": [
{
"cat_id": "1",
"category_name": "Wedding Venues",
"status": "1",
"order_id": "1",
"category_type_id": "1"
},
I wanted to filter my 2nd Observable data to only return values that matches category_type_id from 1st Observable
My Code
Observable obsService = retrofitService.loadService(getSharedPref().getVendorId());
Observable obsCategory = retrofitService.loadCategory();
Observable<ServiceAndCategory> obsCombined = Observable.zip(obsService.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()), obsCategory.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()), new Func2<ServiceModel, CategoryModel, ServiceAndCategory>() {
#Override
public ServiceAndCategory call(ServiceModel serviceModel, CategoryModel categoryModel) {
return new ServiceAndCategory(serviceModel, categoryModel);
}
});
obsCombined.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
obsCombined.subscribe(new Subscriber<ServiceAndCategory>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
if (e instanceof UnknownHostException || e instanceof ConnectException) {
mPresenter.onNetworkError();
} else if (e instanceof SocketTimeoutException) {
mPresenter.onTimeOutError();
} else {
mPresenter.onServerError();
}
}
#Override
public void onNext(ServiceAndCategory model) {
mPresenter.onSuccess(model);
}
});
EDIT
basically I want to apppy the following logic
this.categoryList = combinedModel.categoryModel.getData();
serviceList = combinedModel.serviceModel.getData().getCategories();
for (int i = 0; i < serviceList.size(); i++) {
for (int j = 0; j < categoryList.size(); j++) {
if (!serviceList.get(i).getCategoryTypeId().equals(categoryList.get(j).getCategoryTypeId())) {
categoryList.remove(j);
}
}
}
You can apply this filtering with reactive approach using a map and a list, first collect all categories to a map, and all services to a list, zip them together, and then filter the services list according to categories map:
Observable<HashMap<Integer, CategoryData>> categoriesMapObservable =
obsCategory
.flatMapIterable(CategoryModel::getData)
.reduce(new HashMap<>(),
(map, categoryData) -> {
map.put(categoryData.getCategoryTypeId(), categoryData);
return map;
}
);
Observable<List<ServiceData>> serviceListObservable = obsService
.map(ServiceModel::getData);
Observable obsCombined =
Observable.zip(
categoriesMapObservable
.subscribeOn(Schedulers.io()),
serviceListObservable
.subscribeOn(Schedulers.io()),
Pair::new
)
.flatMap(hashMapListPair -> {
HashMap<Integer, CategoryData> categoriesMap = hashMapListPair.first;
return Observable.from(hashMapListPair.second)
.filter(serviceData -> categoriesMap.containsKey(serviceData.getCategoryTypeId()))
.toList();
}, (hashMapListPair, serviceDataList) -> new Pair<>(hashMapListPair.first.values(), serviceDataList));
the output result depends on you , here I apply at the end a selector of flatMap() that will create a Pair of Collection of CategoryData and a filtered list of ServiceData, you can of course create whatever custom Object you need for that.
I'm not sure you're gaining much from this, it's seems more efficient from complexity perspective, assuming HashMap is O(1), where categories are N, and services are M, you have here N + M (N constructing the map, M iterating the list and querying the map), while your naive implementation will be N x M.
as for code complexity, i'm not sure it worth it, you can apply your logic at the end of the zip for filtering, or use some library that might be doing filter more efficiently.
P.S the observerOn(AndroidSchedulers.mainThread() is unnecessary so I removed it.