merge two lists of different objects - java

i'm using api requests that returns a list.
-the first api request returns a list of object that contains (user_id,content,date,title)
-the second response returns list of object too that contains (user_id,user_name).
i want to merge the two list the display them into one recycler view but keep user name instead of user_id.this image breaks down what i want clearly.
apprecuiate any help i'm really stuck in this and i need it ty .
EDIT
this is the first api call :
followuplist=new ArrayList<>();
Retrofit retrofit = RetrofitInstance.getRetrofitInstance();
final Api api = retrofit.create(Api.class);
Call<List<TraitementTicketModel>> call = api.getfollowup(id, sestoken);
call.enqueue(new Callback<List<TraitementTicketModel>>() {
#Override
public void onResponse(Call<List<TraitementTicketModel>> call, Response<List<TraitementTicketModel>> response) {
if (!response.isSuccessful()) {
Toast.makeText(getApplicationContext(), "Something is wrong !! ", Toast.LENGTH_LONG).show();
Log.e("TAG", "onResponse: something is wrong");
} else if (response.body() == null) {
return;
}
List<TraitementTicketModel> followups = response.body();
for (TraitementTicketModel followup : followups) {
followuplist.add(followup);
}
followuplist.add(firstfollowup());
}
#Override
public void onFailure(Call<List<TraitementTicketModel>> call, Throwable t) {
Toast.makeText(getApplicationContext(),"Pas de connextion internet",Toast.LENGTH_LONG).show();
}
});
this is the second api call :
List<User> userList;
SharedPreferences sp =getApplicationContext().getSharedPreferences("tokenPref", Context.MODE_PRIVATE);
String sestoken = sp.getString("token","");
Retrofit retrofit= RetrofitInstance.getRetrofitInstance();
final Api api= retrofit.create(Api.class);
Call<List<User>> call = api.getUser(sestoken);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.code() != 200){
Log.e("TAG", "onResponse: something is wrong"+response.code() );
}
List<User> users = response.body();
for (User user : users){
userList.add(user);
}
swipeRefreshLayout.setRefreshing(false);
}
so I have two liststhe first one is :
followuplist (user_id,title,content,date)
and the second :
userList(user_id,user_name)
but i didn't know what to do after that to get to my goal

You can do something like that.
In this example UserDetails is the object on the left in your image, UserInfo the one on the right, and MergeData the result.
You should use Kotlin instead of Java, it's far easier to manipulate lists.
List<MergedData> mergeList(
List<UserDetails> listUserDetails,
List<UserInfo> listUserInfo
) {
// Resulting list
final List<MergedData> result = new ArrayList<>();
// We iterate through the first list
for (UserDetails details : listUserDetails) {
// For each element of the list we will try to find one with the same user id in the other list
for (UserInfo info : listUserInfo) {
// if the current element of the second list has the same user id as the current one from the first list, we merge the data in a new object and this object is then added to the result list.
if (details.getUserId().equals(info.getUserId())) {
result.add(
new MergedData(
info.getName(),
details.getContent(),
details.getTitre(),
details.getDate()
)
);
// Once the object is found it is unnecessary to continue looping though the second list, so we break the for loop.
break;
}
}
}
// Once we finished to iterate through the first list, we return the result.
return result;
}
Same example in Kotlin:
fun mergeList(
listUserDetails: List<UserDetails>,
listUserInfo: List<UserInfo>
): List<MergedData> =
listUserDetails.mapNotNull { details ->
listUserInfo
.firstOrNull { it.userId == details.userId }
?.let { info ->
MergedData(
info.name,
details.content,
details.titre,
details.date
)
}
}

Related

Need to add data returned from api to List, but can only post to LiveData?

From my fragment I call: videoViewModel.fetchContentSections();
From my vm I call: public void fetchContentSections(){repository.getContent();}
From my repo I do this:
apiService.getContent(request).enqueue(new Callback<Content>() {
#Override
public void onResponse(Call<Content> call, Response<Content> response) {
List<Section> sections = response.body() != null ? response.body().getSections() : null;
if (sections != null && !sections.isEmpty()) {
final List<Section> sectionList = new ArrayList<>();
for (Section section : sections) {
sectionList.add(section);
}
}
}
#Override
public void onFailure(Call<Content> call, Throwable t) {
Log.d(TAG, "onFailure" + Thread.currentThread().getName());
}
});
Data is returned, but in this scenario the list is null.
If I substitute the if statement for: sectionsMutableLiveList.postValue(response.body().getSections());
...everything works fine. But I need to use a non-LiveData list so I can then write the sectionList to a file. I'm hoping to then read the list from the file and post the value to a LiveData list to my vm.
Does anyone know what I've done wrong?

Android Firebase Firestore query returns empty results from function, but the same code works when included directly

I'm having a trouble with firebase queries in Studio.
I'm trying to abstract a simple query to get a list of object stored in a collection (in my case "users")
I want to create a function stored in a Class that can be called by every fragment into the project.
But I don' t find any method to do that, is to repeate the same instruction the only way to do that?
Here is an example
db.collection("users") //get all the users
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
if(task.getResult() != null) {
for (QueryDocumentSnapshot document : task.getResult()) {
usersList.add(document.toObject(User.class));
} else {
Log.w(LOGIN, "Error getting documents.", task.getException());
}
});
I write these lines of code every time I need them, but I want to create a method that return a List as in this example:
public static List<User> getUsers(FirebaseFirestore db) {
List<User> usersList = new ArrayList<>();
db.collection("users") //get all the users
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
if(task.getResult() != null) {
for (QueryDocumentSnapshot document : task.getResult()) {
usersList.add(document.toObject(User.class));
} else {
Log.w(LOGIN, "Error getting documents.", task.getException());
}
});
} else {
//error
}
return usersList;
}
Data is loaded from Firestore (and most modern cloud APIs) asynchronously, because it may take some time. Instead of blocking the app during that time, the main code continues to execute. Then when the data is available, your addOnCompleteListener callback is executed with that data.
The easiest way to see this is by adding some well-placed logging to your code:
public static List<User> getUsers(FirebaseFirestore db) {
Log.i(LOGIN, "Starting getUsers");
db.collection("users") //get all the users
.get()
.addOnCompleteListener(task -> {
Log.i(LOGIN, "Got data");
})
Log.i(LOGIN, "Returning from getUsers");
}
When you run this code, you get the following output:
Starting getUsers
Returning from getUsers
Got data
This is probably not the order you expected, but it completely explains why the code that calls getUsers never sees the data: by the time your return usersList runs, the data hasn't loaded yet and usersList.add(document.toObject(User.class)) has never been called.
The solution is always the same: any code that needs the data from the database, must either be inside the completion callback, be called from there, or be synchronized by some other means.
A simple example is to create a custom callback function:
public interface GetUsersCallback {
void onCallback(List<User> users);
}
You then pass that to getUsers, which can then call it once it's gotten and processed the results from the database:
public static void getUsers(FirebaseFirestore db, GetUsersCallback callback) {
// 👆
List<User> usersList = new ArrayList<>();
db.collection("users") //get all the users
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
if(task.getResult() != null) {
for (QueryDocumentSnapshot document : task.getResult()) {
usersList.add(document.toObject(User.class));
} else {
Log.w(LOGIN, "Error getting documents.", task.getException());
}
});
callback(usersList); // 👈
}
}
And you can then use it like this:
getUsers(new GetUsersCallback() {
#Override
public void onCallback(List<User> users) {
Log.i(LOGIN, "Found "+users.size()+" users");
}
});
Asynchronous loading of data is incredibly common when dealing with cloud APIs, but it's also quite confusing when you first encounter it. I recommend reading some of these answers to learn more about it:
How to check a certain data already exists in firestore or not
How to return a DocumentSnapShot as a result of a method?
Why does my function that calls an API return an empty or null value?
Firebase Firestore get data from collection

Api data returning null

I am trying to use Jetpack in my Android development, I am able to display data from the the sports api but some of the data is null and not displaying, I am using data binding and this is was my first instinct check if null during my interview
android:text="#{model.league != null ? model.league : #string/app}"
During my interview however I failed I suppose due to not being able to identify what other location I can check for null data when the I display data. So my question is how do I change this code to ensure no textview is null or how do how I handle null api calls, and where, I just want to be able to not make the same mistake again.
Reponse Class
public LiveData<List<Match>> getEvents(String teamId) {
final MutableLiveData<List<Match>> events = new MutableLiveData<>();
Call<AppResponse> call = RetrofitClient.getEvents(teamId);
call.enqueue(new Callback<AppResponse>() {
#Override
public void onResponse(#NonNull Call<AppResponse> call, #NonNull Response<AppResponse> response) {
if (response.isSuccessful()) {
AppResponse appResponse = response.body();
if (appResponse != null)
events.setValue(appResponse.getEvents());
} else
Log.i(TAG, "Error: " + response.errorBody());
}
#Override
public void onFailure(#NonNull Call<AppResponse> call, #NonNull Throwable t) {
Log.i(TAG, "Error " + t.getMessage());
}
});
return events;
}

how to url calling return from list function with retrofit

i tried to return list from the url that i get with retrofit. it works and i get the data but it wont return.
this is my code
public List<MovieResponse> loadCourses() {
ArrayList<MovieResponse> list = new ArrayList<>();
ApiServices apiService =
NetworkClient.getRetrofitClient().create(ApiServices.class);
Call<MovieResult> call = apiService.getMovies();
call.enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.body() != null) {
ArrayList<MovieResponse> movies = new ArrayList<>();
movies = response.body().getResults();
Log.d("",""+movies);
list.addAll(movies);
Log.d("",""+list);
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
// Log error here since request failed
Log.e("error", t.toString());
}
});
return list;
}
when i print list inside onResponse it works and there are the data. but when i return it or trying to print list outside onResponse for example below ArrayList<MovieResponse> list = new ArrayList<>(); it not show the data.
please help what is actually wrong with it. i really appreciate it.
The simplest way is to define your movies list directly inside activity or fragment(in other words, a field member of the class).
It's not a good idea to return data from an asynchronous method.
Change the return type of the loadCourses method to void and instantiate the filed movies inside onResponse().
public class SomeActivity extends AppCompatActivity {
private ArrayList<MovieResponse> movies = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_some);
}
public void loadCourses() {
ApiServices apiService =
NetworkClient.getRetrofitClient().create(ApiServices.class);
Call<MovieResult> call = apiService.getMovies();
call.enqueue(new Callback<MovieResult>() {
#Override
public void onResponse(Call<MovieResult> call, Response<MovieResult> response) {
if (response.body() != null) {
movies = response.body().getResults();
...
}
}
#Override
public void onFailure(Call<MovieResult> call, Throwable t) {
...
}
});
}
}
It is because you are making asynchronous call which is being handled by a separate thread. So after call.enqueue(), the main thread directly jumps to return statement without waiting for API response, that's why you are getting empty list.
Assuming your API takes 1 sec to respond,
just for an experiment, you can try adding a sleep() for 3 sec right before your return statement, it should return all the movies.
If you must return from the method then go for retrofit synchronous call.
To make a sync call create a new thread in main thread and make call from there, it is not allowed to make network call from main thread because it blocks the thread.

Parse: Duplicate records for the a single saveInBackground Call

I have a single incident where a complete duplicate of a entry was made into the database (the same user comment appeared twice). They had different object IDs but were otherwise the exact same. It was slower than usual to finish the posting and only happened once out of dozens of comments, so I want to say it was a Parse issue during the saveInBackground call. Even so, I expect a service like Parse to be a little more robust. As my first time working with Android though, I also can't be sure nothing is wrong on my end. Any help? Also just any criticisms? This is the method called when the user hits a comment submission button:
private void submitComment() {
String text = commentText.getText().toString().trim();
Intent intent = getIntent();
String ID = intent.getStringExtra("imageID");
String parentID = intent.getStringExtra("parent");
// Set up a progress dialog
final ProgressDialog loadingDialog = new ProgressDialog(CommentSubmitActivity.this);
loadingDialog.setMessage(getString(R.string.publishing_comment));
loadingDialog.show();
Comment comment = new Comment();
comment.setText(text);
comment.setUser((ParseUser.getCurrentUser()));
if (ID.equals("#child")) {
comment.setParent(parentID);
comment.setImage("#child");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.getInBackground(parentID, new GetCallback<ParseObject>() {
public void done(ParseObject parentComment, ParseException e) {
if (e == null) {
int numChild = parentComment.getInt("numChild");
parentComment.put("numChild", ++numChild);
parentComment.saveInBackground();
} else {
Log.d("numChild: ", "error");
}
}
});
} else {
comment.setImage(ID);
comment.put("numChild", 0);
ParseQuery<ParseObject> query = ParseQuery.getQuery("ImageUpload");
query.getInBackground(ID, new GetCallback<ParseObject>() {
public void done(ParseObject image, ParseException e) {
if (e == null) {
int numComments = image.getInt("numComments");
image.put("numComments", ++numComments);
image.saveInBackground();
} else {
Log.d("numComments: ", "error");
}
}
});
}
comment.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
loadingDialog.dismiss();
finish();
}
}
});
}
I encountered similar problem like yours.
I created an app where user can create account and add photo to it and list of objects (friends in my case).
Once when I was testing it user was created twice.
I went through my code and my my suspicions are connected with async calls.
I see that you use asynchronous parse api in you application so no fragment of code is waiting for response and blocking the rest of operations.
You cannot control when parse server will response.
What I did I just put all synchronous requests in my custom async code (AsyncTask in Android).
Hope that my answer somehow meeets your expectations.

Categories

Resources