I'm trying to retrieve a value from an Object which sits within an ArrayList
within another Object.
The basic structure is:
+++++
Book Object -> List of Author Objects -> Author Object -> Author first name variable
+++++
And I would like to access the first name of the first Author for a given Book
+++++
I have created a "Book" Class which looks like the following:
public class Book {
private String mTitle;
private List<Author> mAuthors;
public Book(String title, List<Author> authors) {
this.mTitle = title;
this.mAuthors = authors;
}
public String getmTitle() {
return mTitle;
}
public List<Author> getmAuthors() {
return mAuthors;
}
}
This class also contains a list of Author-Objects:
public class Author {
private String mFirstName;
private String mLastName;
public Author(String firstName, String lastName) {
this.mFirstName = firstName;
this.mLastName = lastName;
}
public String getmFirstName() {
return mFirstName;
}
public String getmLastName() {
return mLastName;
}
}
I then create an the list of Author instances in the MainActivity:
ArrayList<Author> authors = new ArrayList<>();
authors.add(new Author("Hans", "Schwabe"));
And use this list when creating the book instance
Book buch = new Book("Säulen der Erde",authors);
When I then try to access the name of the first Author in the list I use the following code:
List<Author> authorArrayList = new ArrayList<Author>();
authorArrayList = buch.getmAuthors();
authorArrayList.get(1).getmFirstName();
And at this point my app keeps crashing.
**
Hence: What would be the right way to retrieve the first name of the
first author from the list?
**
Issue is that you have 1 author and you are trying to retrieve it from index 1. Index should be 0. Indexing in programming is starting from 0.
If you write
authorArrayList.get(0).getmFirstName();
It should work
Related
I have a RealmObject, which is used as a temporary data cache only (there will be many entries). I also wrote a static method add() so I can easily add a new entry, but it seems too complicated. Here is the whole class:
public class ExchangePairPriceCache extends RealmObject {
#Index
private String exchangeName;
#Index
private String baseCurrency;
#Index
private String quoteCurrency;
private float price;
private long lastPriceUpdate;
public ExchangePairPriceCache() {
exchangeName = "";
baseCurrency = "";
quoteCurrency = "";
price = 0;
lastPriceUpdate = 0;
}
public ExchangePairPriceCache(String exchangeName, String baseCurrency, String quoteCurrency) {
this.exchangeName = exchangeName;
this.baseCurrency = baseCurrency;
this.quoteCurrency = quoteCurrency;
price = 0;
lastPriceUpdate = 0;
}
public void setPrice(float price) {
// this needs to be called inside a Realm transaction if it's a managed object
this.price = price;
lastPriceUpdate = System.currentTimeMillis();
}
public float getPrice() {
return price;
}
/* static functions */
public static void add(String exchangeName, String baseCurrency, String quoteCurrency, float price) {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(r -> {
ExchangePairPriceCache priceCache = r.where(ExchangePairPriceCache.class)
.equalTo("exchangeName", exchangeName)
.equalTo("baseCurrency", baseCurrency)
.equalTo("quoteCurrency", quoteCurrency).findFirst();
if(priceCache != null) {
priceCache.setPrice(price);
} else {
priceCache = new ExchangePairPriceCache(exchangeName, baseCurrency, quoteCurrency);
priceCache.setPrice(price);
ExchangePairPriceCache finalPriceCache = priceCache;
r.insert(finalPriceCache);
}
});
realm.close();
}
public static ExchangePairPriceCache get(String exchangeName, String baseCurrency, String quoteCurrency) {
Realm realm = Realm.getDefaultInstance();
ExchangePairPriceCache priceCache = realm.where(ExchangePairPriceCache.class)
.equalTo("exchangeName", exchangeName)
.equalTo("baseCurrency", baseCurrency)
.equalTo("quoteCurrency", quoteCurrency)
.greaterThan("lastPriceUpdate", System.currentTimeMillis() - TimeUnit.SECONDS.toMillis(10)).findFirst();
if(priceCache != null)
priceCache = realm.copyFromRealm(priceCache);
realm.close();
return priceCache;
}
public static void deleteAll() {
Realm realm = Realm.getDefaultInstance();
realm.executeTransaction(r -> r.delete(ExchangePairPriceCache.class));
realm.close();
}
}
Questions:
Is this a good design (having static functions for ease of use)? I like how I can insert new entries into cache like ExchangePairPriceCache.add("NASDAQ", "AAPL", "USD", 100.5); and delete all with ExchangePairPriceCache.deleteAll() when needed.
How can I simplify add() function? Right now I check if entry already exists and then update the price and if it doesn't, I create a new object and insert it into Realm. I am not able to use updateOrInsert because I don't have unique index for object.
Maybe I am just questioning myself too much and this is all good as it is. But I'd really appreciate some input from experts who use it daily.
You should use a "Repository design pattern" with a DAO object (Data Access Object), to do all your read/ write transactions in realm.
Model class should be a blind copy of objects just holding entities.
Since you do not have any unique identifiers, you can try below
Cache the Exchange pair in Shared preferences file (if they are added earlier or not)
For faster read/writes : Create a temporary unique identifier with a combination of key-value pair that you already have
eg : (exchangeName + baseCurrency + quoteCurrency) - Cast into proper formats to create some unique key with all these values.
I want to cache a song-list in my app, the Song-list structure is like below:
#Entity
public class Songlist {
String _id;
String desc;
List<SongDesc> songWithComment;
.....
public static class SongDesc {
String comment;
Song song;
}
}
#Entity
pulbic class Song {
String name;
String type;
......
}
The lib of operating sqlite3 is android.arch.persistence.room, but it dosen't allow object references in a table.Is there any way to cache a song-list by using Room in Android?
Also if you want to store some custom objects, you can use #Embedded annotation like in example bellow :
class Address {
public String street;
public String state;
public String city;
#ColumnInfo(name = "post_code")
public int postCode;
}
#Entity
class User {
#PrimaryKey
public int id;
public String firstName;
#Embedded
public Address address;
}
If you want to save an ArrayList of some type into the database, you shuld use TypeConverter to convert the ArrayList from and into a simpler recognizable type to the database engine, like a String.
See this:
Android Room Database: How to handle Arraylist in an Entity?
and https://commonsware.com/AndroidArch/previews/room-and-custom-types
I'm trying to get myself into RxJava so I read some posts about it. I think I understood how it works but I would like to submit you a theorical code to ensure my good understanding of the library.
Let's imagine I have an API enabling to retrieve a list of produceur and for each of them the artists they produce, their albums and their songs.
My model would be the following
public class Produceur {
private Integer id;
private String name;
private String pictureUrl;
}
public class Artist {
private Integer id;
private String name;
private String lastname;
private String sceneName;
private String pictureUrl;
}
public class Album {
private Integer id;
private int year;
private String name;
private String style;
private String pictureUrl;
private Integer artistId;
}
public class Song {
private Integer id;
private String title;
private int duration;
private String pictureUrl;
private Integer albumId;
}
With my Retrofist services
#GET("myUrl/produceurs")
Observable<List<ProduceurResponse>> getProduceurs();
#GET("myUrl/produceurs/{produceurId}")
Observable<List<ArtisteResponse>> getArtistForProduceur(#Path("produceurId") Integer produceurId);
#GET("myUrl/picture/{id}")
Observable<ResponseBody> getPicture(#Path("id") Integer id);
And the response objects
public class ProduceurResponse {
private Integer id;
private String name;
private String pictureUrl;
}
public class ArtisteResponse {
private Integer id;
private String name;
private String lastname;
private String sceneName;
private String pictureUrl;
private List<AlbumResponse> albums;
}
public class AlbumResponse {
private Integer id;
private int year;
private String name;
private String style;
private String pictureUrl;
private Integer artistId;
private List<SongResponse> songs;
}
public class SongResponse {
private Integer id;
private String title;
private int duration;
private String pictureUrl;
private Integer albumId;
}
I would like to retrieve all the produceur and for each the artists and save all the images in the local memory in the same sequence.
I thought about the following code where for each produceur we will retrieve the artists, their albums and song and insert them into our base.
And once we've finished retrieveing the produceurs and artists we can download images (we stored in a list every id with a picture).
getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
.doOnNext(produceur -> insertProduceurInBase(produceur))
.subscribe(produceur -> Observable.from(getArtistForProduceur(produceur.getId())
.flatMap(artists -> Observable.from(artists))
.doOnNext(artist -> insertArtistInBase(artist)))
.subscribe(),
e -> showError(e),
() -> Observable.from(listOfIdsToDownload)
.doOnNext(id -> getPicture(id))
.subscribe(response -> createImage(response),
e -> showError(e),
() -> isFinished()
)
);
Would this code work (I think so but I'm not sure) ? Is this the best way to do it ?
Now, what if my getProduceurs service return me a list of ProduceurResponse containing produceur artists ids and I got a service to retrieve the artist profil and another one to retrieve its albums.
public class ProduceurResponse {
private Integer id;
private String name;
private String pictureUrl;
List<Integer> artistsIds;
}
public class ArtisteProfileResponse {
private Integer id;
private String name;
private String lastname;
private String sceneName;
private String pictureUrl;
}
#GET("myUrl/artist/{artistId}")
Observable<List<ArtisteProfileResponse>> getArtistProfile(#Path("artistId") Integer artistId);
#GET("myUrl/artist/{artistId}/detail")
Observable<List<AlbumResponse>> getArtistAlbums(#Path("artistId") Integer artistId);
I could to use the .zip to make the getArtistProfile() and getArtistAlbums() call simultaneous with something like
getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
.doOnNext(produceur -> insertProduceurInBase(produceur))
.subscribe(produceur -> Observable.from(produceur.getArtistIds())
.zip(
getArtistProfile(),
getArtistAlbums(),
(artistProfil, albumList) -> insertArtistInBase()
)
.subscribe(),
e -> showError(e),
() -> Observable.from(listOfIdsToDownload)
.doOnNext(id -> getPicture(id))
.subscribe(response -> createImage(response),
e -> showError(e),
() -> isFinished()
)
);
But I'm really not sure I'm using the zip the right way. Is zip well used in this piece of code ? Would this work ? Is this the best way to do it ?
EDIT
So I tried to implement something similar to my original idea with the google books api.
I've got a Retrofit interface
public interface IBookService {
#GET("volumes?q=robot+subject:fiction")
Observable<BookSearchResult> getFictionAuthors(#Query("category") String key);
#GET("volumes")
Observable<BookSearchResult> getBooksForAuthor(#Query("q") String author, #Query("category") String key);
}
With
public class BookSearchResult {
public List<BookResult> items;
}
public class BookResult {
public String id;
public String selfLink;
public VolumeInfoResult volumeInfo;
public SaleInfoResult saleInfo;
}
And I try to retrieve the books of fiction with a the string robot (getFictionAuthors) with return me a BookSearchResult containing a list of BookResult. For each of the book results I retrieve all the books of the author with getBooksForAuthor. My code is below
Observable<BookResult> observable = mWebService.getFictionAuthors(API_KEY)
.flatMap(new Func1<BookSearchResult, Observable<BookResult>>() {
// Parse the result and build a CurrentWeather object.
#Override
public Observable<BookResult> call(final BookSearchResult data) {
return Observable.from(data.items);
}
})
.concatMap(new Func1<BookResult, Observable<BookSearchResult>>() {
// Parse the result and build a CurrentWeather object.
#Override
public Observable<BookSearchResult> call(final BookResult data) {
return mWebService.getBooksForAuthor("=inauthor:" + data.volumeInfo.authors.get(0), API_KEY);
}
})
.flatMapIterable(new Func1<BookSearchResult, List<BookResult>>() {
// Parse the result and build a CurrentWeather object.
#Override
public List<BookResult> call(final BookSearchResult data) {
return data.items;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BookResult>() {
#Override
public void onNext(final BookResult book) {
Log.e("Book","Book is " + book.volumeInfo.title + " written by " + book.volumeInfo.authors.get(0));
}
#Override
public void onCompleted() {
Log.e("Book","Book list completed");
}
#Override
public void onError(final Throwable error) {
Log.e("Book","Book list error");
}
}
This code is working but there is something weird that I don't understand. In my logs I have at first the return from the getBooksForAuthor request for the first author and then the log from each book of that author. After that I've got the result of the request for the second author and part of the log from his books. Follow the results of the request of the other authors and then the end of the list of books for the second author and the list of books for all the other authors.
To illustrate it, my logs looks like
- > Return from request for Author 1
- > Book 1 from author 1
...
- > Book 10 from author 1
- > Return from request for Author 2
- > Book 1 from author 2
...
- > Book 5 from author 2
- > Return from request for Author 3
- > Return from request for Author 4
...
- > Return from request for Author 10
- > Book 6 from author 2
..
- > Book 10 from author 2
- > Book 1 from author 3
..
- > Book 10 from author 3
...
- > Book 1 from author 10
..
- > Book 10 from author 10
When I expected
- > Return from request for Author 1
- > Book 1 from author 1
...
- > Book 10 from author 1
- > Return from request for Author 2
- > Book 1 from author 2
...
- > Book 10 from author 2
...
- > Return from request for Author 10
- > Book 1 from author 10
...
- > Book 10 from author 10
Does someone have an explanation or understand what I'm missing ?
You should avoid nested subscription (check out the flatmap operator). It's offen an indicator of a code smell.
To avoid nested subscription, you can use the operator flatMap (do not garanti resulting events order) or concatMap (garanti resulting events order).
I notice too that you're using another Observable in the completed callback : you can concat observables in this case.
So the rework of your code using this sort of operators :
getProduceurs().flatMap(produceurs -> Observable.from(produceurs))
.doOnNext(produceur -> insertProduceurInBase(produceur))
// call getArtistForProduceur and emit results in order
.concatMap(produceur -> getArtistForProduceur(produceur.getId()))
// emits items of the list
.flatMapIterable(artists -> artists)
.doOnNext(artist -> insertArtistInBase(artist)))
// don't care about elements. But will wait for the completion of the previous observable
.ignoreElements()
// perform jobs after the previous observable complete
.concatWith(Observable.from(listOfIdsToDownload)
.doOnNext(id -> getPicture(id))
.doOnNext(response -> createImage(response)))
// show an error if an error occur in the downstream
.doOnError(e -> showError(e))
// call isFinished when everything is finished.
.doOnCompleted(() -> isFinished())
.subscribe()
Hi i have and android app with model TopStory. I want create a collection of TopStory (topStories) which order the item by value (ie.time) Whenever new item is added. the new added item will be at the correct index (order by value time) (ie: create an already sorted Collection so that whenever we add new item, it will automatically be inserted to the right position)
Here is my model
public class TopStory {
private int id;
private String title;
private String author;
private int score;
private JSONArray kids;
private long time;
private String url;
public TopStory() {
}
public TopStory(int id, String title, String author, int point, long time,String url) {
this.id = id;
this.title = title;
this.author = author;
this.score = point;
this.time = time;
this.url = url;
}
What should I use? PriorityQueue, TreeMap,...? how to create that type of collection? Any help is much appreciate. Thanks.
You can use TreeMap data structure http://developer.android.com/reference/java/util/TreeMap.html
Find how it will work for you,
http://www.java2novice.com/java-collections-and-util/treemap/comparator-user-object/
TreeMap<Empl,String> tm = new TreeMap<Empl, String>(new MyNameComp());
tm.put(new Empl("Ram",3000), "RAM");
tm.put(new Empl("John",6000), "JOHN");
tm.put(new Empl("Crish",2000), "CRISH");
tm.put(new Empl("Tom",2400), "TOM");
Set<Empl> keys = tm.keySet();
for(Empl key:keys){
System.out.println(key+" ==> "+tm.get(key));
}
If I am reading your question right, you have an object, and you want to sort it into a data structure based on an integer property. In this case I recommend implementing a binary tree.
I'm using realm to store my data on Android. Awesome framework! Now the only problem I'm now having is:
I got a array list strings with id's of Countries in my database.
Now I retrieve my Drinks that contains a relationship to countries.
Is there a way that I could to do a query like this:
String [] ids;
realm.where(Drinks.class).equalsTo("country.id", ids);
Something like that?
Or do I really need to do a query to get me all drinks and then filter the list manually?
EDIT:
My classes:
public class Drinks extends RealmObject {
#PrimaryKey
private String id;
private String name;
private Country country;
}
public class Country extends RealmObject {
#PrimaryKey
private String id;
private String name;
}
What you want to do is possible with link queries in theory (searching for "country.id"), however link queries are slow. Also you'd need to concatenate a bunch of or() predicates together, and I would not risk that with a link query.
I would recommend using the following
public class Drinks extends RealmObject {
#PrimaryKey
private String id;
private String name;
private Country country;
#Index
private String countryId;
}
public class Country extends RealmObject {
#PrimaryKey
private String id;
private String name;
}
And when you set the Country in your class, you also set the countryId as country.getId().
Once you do that, you can construct such:
RealmQuery<Drinks> drinkQuery = realm.where(Drinks.class);
int i = 0;
for(String id : ids) {
if(i != 0) {
drinkQuery = drinkQuery.or();
}
drinkQuery = drinkQuery.equalTo("countryId", id);
i++;
}
return drinkQuery.findAll();
Since the Realm database has added RealmQuery.in() with the version 1.2.0
I suggest using something like this.
//Drinks
public class Drinks extends RealmObject {
#PrimaryKey
private String id;
private String name;
private String countryId;
//getter and setter methods
}
//Country
public class Country extends RealmObject {
#PrimaryKey
private String id;
private String name;
//getter and setter methods
}
The code to use inside activity/fragments to retrieve drink list
String[] countryIdArray = new String[] {"1","2","3"} //your string array
RealmQuery<Drinks> realmQuery = realm.where(Drinks.class)
.in("countryId",countryIdArray);
RealmResults<Drinks> drinkList = realmQuery.findAll();
In latest version of Realm 7+, you can use anyOf to match a field against a list of values.
anyOf("name", new String[]{"Jill", "William", "Trillian"})
in older versions, use in instead of anyOf and with kotlin use oneOf instead of in.
see this issue
To match a field against a list of values, use in. For example, to find the names “Jill,” “William,” or “Trillian”, you can use in("name", new String[]{"Jill", "William", "Trillian"}). The in predicate is applicable to strings, binary data, and numeric fields (including dates).
Doc.-> https://realm.io/docs/java/latest#queries