I have an array of Users in a class, under the column "likers"
I want to check whether or not the current user is contained in the likers array, in minimal time
This is my current (dysfunctional) code:
ArrayList<ParseUser> likers = (ArrayList<ParseUser>) rating.get("likers");
Boolean contained = likers.contains(ParseUser.getCurrentUser()) //always returns false
How can I change it to make it work? I feel it's a match on ObjectId. Maybe there is a function in the Parse SDK?
Thanks!
You can use next approach.
ParseObject rating = ParseObject.create(ParseObject.class);
ArrayList<ParseUser> likers = (ArrayList<ParseUser>) rating.get("likers");
Boolean contained = containsUser(likers, ParseUser.getCurrentUser());
Method
private boolean containsUser(List<ParseUser> list, ParseUser user) {
for (ParseUser parseUser : list) {
if (parseUser.hasSameId(user)) return true;
}
return false;
}
Little bit about your code. It works correct, return false. Because ArrayList implements the List Interface. If you look at the Javadoc for List at the contains method you will see that it uses the equals() method to evaluate if two objects are the same. Result of rating.get("likers") contains same ParseUsers as ParseUser.getCurrentUser, but they are different objects at memory. So you need find it by comparing ObjectId from Parse.com.
Related
In the below posted code, I would like to know how can I iterate through a list of Optional objects?
According to the code:
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user->user.getOptUserFavoriteTvList()
.filter(list-> list.get(???) != Optional.empty())
);#
Now i want to refer to each index in the list? how can i achieve it?
code:
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user->user.getOptUserFavoriteTvList()
.filter(list-> list.get(0) != Optional.empty())
);
private User getUser_3() {
List<Optional<String>> list = new ArrayList<>(5);
list.add(Optional.of("espn"));
list.add(Optional.of("Cnn"));
list.add(Optional.empty());
list.add(Optional.of("deutsch welle"));
User user = new User();
user.setUserName("johannas");
user.setUserEmailAddres("joha90#gmail.com");
user.setUserId("2005");
user.setUserFavoritesTvList(list);
return user;
}
public Optional<List<String>> getOptUserFavoriteTvList() {
return OptionalsUtils.toOptional(this.getUserFavoriteTvList());
}
after flatMap, extract the list from the optional if present via orElse and then apply your logic...
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user-> user.getOptUserFavoriteTvList())
.orElse(Collections.emptyList());
after the call to orElse, you're now working with a List<String> which you can call stream() upon to perform more complex operations or iterate through it using a typical for loop etc...
you could even go with ifPresent if it's more suitable for the task at hand.
OptionalsUtils.toOptional(this.getUser_3())
.flatMap(user-> user.getOptUserFavoriteTvList())
.ifPresent(list -> list.forEach(e -> ....));
I have a RecyclerView with a FirebaseRecyclerAdapter. I want to populate the RecyclerView with a list of names when the user starts typing into the SearchView.
public class SchoolsAdapter extends FirebaseRecyclerAdapter<School, SchoolsAdapter.SchoolViewHolder> {
public SchoolsAdapter(Query ref) {
super(School.class, R.layout.item_school, SchoolViewHolder.class, ref);
}
#Override
public void populateViewHolder(SchoolViewHolder schoolViewHolder, School school, int position) {
schoolViewHolder.name.setText(school.getName());
schoolViewHolder.address.setText(school.getAddress());
}
static class SchoolViewHolder extends RecyclerView.ViewHolder {
public TextView name;
public TextView address;
public SchoolViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.school_item_tview_name);
address = (TextView) itemView.findViewById(R.id.school_item_tview_address);
}
}
}
I'm guessing I need to add a QueryTextListener to the searchview that would update the Query in the adapter. Would this break the FirebaseRecyclerAdapter?
Or should I
#Override
public boolean onQueryTextChange(String newText) {
mRecyclerView.setAdapter(new SchoolAdapter(ref.orderByChild("name").startAt(userQuery).endAt(userQuery+"~"))
return false;
}
whenever the user types something?
Also the docs talk about ordering and sorting firebase queries but don't explicitly say the best way to do string pattern matching. What's the best way to do string matching so that the recycler view shows all results which have the search query as a substring of the database record, and possibly those that are 1 edit distance away as well.
Also a way to ignorecase on queries?
I just finished doing something near to what you're looking for, I'm not sure it's the most elegant solution, but I'll throw out some ideas and if you think my idea will help I can definitely provide some examples.
Firstly, when I extended the base FirebaseAdapter I added a new filter named mFullList, since mItems of the FirebaseAdapter will be used for the display list, I don't want to keep going back to the network when I didn't have to. I then override all the methods in my child class to update mFullList with the values from the Firebase callbacks, sort them, filter them then call super.X() with the new list.
Quickly:
public reset(List)
mFullList = List
Collections.sort(mFullList, Comparator)
getFilter().filter(filterString)
The filterString is a field within the Adapter and is updated during the call to getFilter().filter(). During the perform filter I then loop through the mFullList and do a compare of:
mFullList.get(pos).getName().toLowerCase().contains(filterString.toLowerCase);
Once fitlering is done you have a new list that is passed to Filter.publishResults in the FilterResults object. publishResults calls a method in the class that performs the update and notify.
filterCompleted(List)
getItems().clear
getItems().addAll
notify
Essentially, I didn't want the FirebaseAdapater to stop getting the full list of items, I just wanted the users request to filter that full list and handle their request appropriately. Also, I didn't see a point to the added network requests based the user typing an extra character.
Using this method you can just use:
adapter.getFilter().filter("something")
to filter the list based on your updated field, and
adapter.getFilter().filter("")
to reset the full list (as long as your performFilter() handled it correctly. This way new updates from FireBase will be filtered based on the users selection, as well as when a user types in new values, won't require making new Firebase network requests.
I'm working on an app in android studio. The part of the app I'm having issues with is where the user can favourite and remove their favourite item. I do this by adding and removing the item from a list.
The thing is the add functionality works which is:
public void addFavorite(Context context, NewSubject subject) {
List<NewSubject> favorites = getFavorites(context);
if (favorites == null)
favorites = new ArrayList<NewSubject>();
favorites.add(subject);
saveFavorites(context, favorites);
}
I am passing in an object of type "NewSubject" which is just a class of getters and setters for name and id.
The problem arises when I try to remove an item from this list. Code below:
public void removeFavorite(Context context, NewSubject subject) {
ArrayList<NewSubject> favorites = getFavorites(context);
if (favorites != null) {
favorites.remove(subject);
saveFavorites(context, favorites);
}
}
I've even tried something like:
for(int i = 0; i < favorites.size(); i++){
if(favorites.get(i).getSubject_name() == subject.getSubject_name())
favorites.remove(i);
}
Even though both subject names match, the if statement never triggers as true. By changing it to ID it does remove the item but I was wondering why it doesn't work the other way. MeetTitan suggested to use "equals" operator to compare Strings and this has fixed that issue. But I'm still wondering as to why removing the item by "subject" without the FOR loop and IF statement doesn't work.
I have cleared the app's data multiple times whilst trying to debug the source of the problem.
Thank you for your time and help, it is much appreciated.
This applies if you are re-creating NewSubject twice... If you are trying to remove the exact same instance of NewSubject that you got from the collection, then I guessed wrong and this isn't the answer you are looking for.
Is it possible you haven't defined equals and hashCode in your Favorites object? Without those remove will only work with the EXACT same object instance in the collection. If you haven't, try defining them and see if remove() works the way you expect.
Without those methods defined, collections will respond this way:
Obj x=new Obj("data")
Obj y=new Obj("data")
collection.put(x)
collection.remove(y)
assert( collection.size() == 1) // was not removed because .equals didn't exist--remove reverted to == instead which failed, x != y
collection.remove(x)
assert( collection.size() == 0) // NOW it worked because you used the same instance.
if you define .equals and hashCode to compare the strings inside obj, then this will work:
collection.put(x)
collection.remove(y)
assert( collection.size() == 0) // worked because x.equals(y)!
Try
String.equalsIgnoreCase(value1,value2)
This might do your work.
From your example, it's evident that name is a String object. In java, you have to use ".equals()" or comparing two strings.
You can do this:
if(favorites.get(i).getSubject_name().equals(subject.getSubject_name())){
...
}
Or, you can override the equals() method in your NewSubject class to make this work:
favorites.remove(subject);
You can use something like this as your equals() method in the NewSubject class (considering you are only matching two NewSubject objects based on their names):
#Override
public boolean equals(Object other){
if (other == null) return false;
if (other == this) return true;
NewSubject otherSubject = (NewSubject) other;
if(this.getSubject_name().equals(otherSubject.getSubject_name()))
return true;
else
return false;
}
Update:
You may want to override hashcode() as well. If your NewSubject class ever gets used in a hash-based collection such as HashMap, overriding only equals() method will not be sufficient. For reference, this is from Effective Java by Joshua Bloch:
You must override hashCode() in every class that overrides equals().
Failure to do so will result in a violation of the general contract
for Object.hashCode(), which will prevent your class from functioning
properly in conjunction with all hash-based collections, including
HashMap, HashSet, and Hashtable.
I'm unable to properly fetch a ParseObject that contains a field of type 'Object' : after changing manually the 'Object' field value in the Parse DataBrowser and then fetch the ParseObject from the app, the fetched ParseObject still provide the old value for the 'Object' field, but provide the right new value for the 'String' field.
Here is the sample code I use :
public class MainActivity extends ActionBarActivity {
ParseObject object;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
object = ParseObject.createWithoutData("Test", "tvgTg8jAXz");
}
#Override
protected void onResume() {
super.onResume();
object.fetchInBackground().onSuccess(new Continuation<ParseObject, Object>() {
#Override
public Object then(Task<ParseObject> task) throws Exception {
JSONObject data = task.getResult().getJSONObject("data");
String name = task.getResult().getString("name");
Log.d("OBJECT", data.toString());
Log.d("OBJECT", name);
return null;
}
}).continueWith(new Continuation<Object, Object>() {
#Override
public Object then(Task<Object> task) throws Exception {
if (task.getError() != null) {
Log.e("OBJECT", task.getError().getLocalizedMessage());
}
return null;
}
});
}
}
After I change both 'data' and 'name' fields in the DataBrowser, if 'onResume()' is called without a previous call to 'onCreate()' (after locking/unlocking screen for example) then the logs shows the old value for 'data' and the new value for 'name'.
This is a simple code example to highlight the problem I encounter in a bigger project.
Is this a known issue of the Parse Android SDK ? Is there a workaround ?
Thanks
Now that I learned that you have turned on the local datastore I can come with an, at least partial, answer.
Turning on the local datastore has some side effects. One being that only one instance of each object exists locally. So when you call fetchInBackground the second time, object is already populated with data. The problem then (i think) is that the API no longer override 'complex' types (pointers, objects, arrays), perhaps because it could mess up internal relationships in the data store. Since the fact that the data store will recursively save an object (and pointers) so suddenly swapping a pointer might leave objects 'hanging'. (again, only guessing).
Now I must admit that it still confuses me a bit looking at your code, cause it does not seem that you at any point write your object to the data store, however..
What should work is to unpin the object before 'refreshing' it:
object.unpinInBackground.onSuccess(new Continuation<>{
...
// when done call fetch
});
According to Parse, this is a known issue that they will not fix for now : https://developers.facebook.com/bugs/1624269784474093/
We must use the following methods to retrieve JSON objects/arrays fields from a ParseObject :
getMap() instead of getJSONObject()
getList() instead of getJSONArray()
These methods will return Map and List objects respectively.
I found that managing Map and List in my project instead of JSONObjet and JSONArray is not a problem and is even clearer.
i made a listview with all the posts in the list.
what i want is when i click the child in the list i want another activity to be opened showing that specific post and the related comments
the question is how to know which item is clicked and how to show that particular post ParseObject in next activity
as they do in messaging app in which you click the message from the listview and subsequent messages are shown in the next activity
i might be very thankful to you if you solve this for me!!
Please Try this code:
Please implement your object class with Serializable
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> adapter, View v, int position,
long arg3) {
try
{
Log.v("position",position); // hear is your list item position
MyClass obj = new MyClass(); // Class must be implements with Serializable
Intent showintent = new Intent(context,<activity class to open>);
showcontactintent.putExtra("obj",obj);
startActivity(showintent);
}
catch(Exception e)
{
e.printStackTrace();
}
}
});
Use: Relational Data
Objects can have relationships with other objects. To model this behavior, any ParseObject can be used as a value in other ParseObjects. Internally, the Parse framework will store the referred-to object in just one place, to maintain consistency.
For example, each Comment in a blogging app might correspond to one Post. To create a new Post with a single Comment, you could write:
// Create the post
ParseObject myPost = new ParseObject("Post");
myPost.put("title", "I'm Hungry");
myPost.put("content", "Where should we go for lunch?");
// Create the comment
ParseObject myComment = new ParseObject("Comment");
myComment.put("content", "Let's do Sushirrito.");
// Add a relation between the Post and Comment
myComment.put("parent", myPost);
// This will save both myPost and myComment
myComment.saveInBackground();
You can also link objects using just their objectIds like so:
// Add a relation between the Post with objectId "1zEcyElZ80" and the comment
myComment.put("parent", ParseObject.createWithoutData("Post", "1zEcyElZ80"));
By default, when fetching an object, related ParseObjects are not fetched. These objects' values cannot be retrieved until they have been fetched like so:
fetchedComment.getParseObject("post")
.fetchIfNeededInBackground(new GetCallback<ParseObject>() {
public void done(ParseObject post, ParseException e) {
String title = post.getString("title");
// Do something with your new title variable
}
});
You can also model a many-to-many relation using the ParseRelation object. This works similar to List, except that you don't need to download all the ParseObjects in a relation at once. This allows ParseRelation to scale to many more objects than the List approach. For example, a User may have many Posts that they might like. In this case, you can store the set of Posts that a User likes using getRelation. In order to add a post to the list, the code would look something like:
ParseUser user = ParseUser.getCurrentUser();
ParseRelation<ParseObject> relation = user.getRelation("likes");
relation.add(post);
user.saveInBackground();
You can remove a post from the ParseRelation with something like:
relation.remove(post);
For more read: https://parse.com/docs/android/guide#objects-relational-data
^why did I copy all the words here instead of just providing the link? Because parse links are broken sometimes and doesn't direct you to the section you need (instead it just sends you to https://parse.com/docs/android/guide and because the doc is so large, you won't be able to find it.