Combine the results of 2 API calls fetching different properties for the same objects with RxSwift
I have a model called Track. It has a set of basic and a set of extended properties. List of tracks and their basic properties are fetched with a search API call, then I need to make another API call with those track IDs to fetch their extended properties.
The question is how to best combine the results of both API calls and populate the extended properties into the already created Track objects, and of course match them by ID (which unfortunately is a different property name in both calls' results). Note that there are many more keys returned in the real results sets - around 20-30 properties for each of the two calls.
Track.swift
struct Track: Decodable {
// MARK: - Basic properties
let id: Int
let title: String
// MARK: - Extended properties
let playbackURL: String
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "title"
case playbackUrl = "playbackUrl"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let idString = try container.decode(String.self, forKey: CodingKeys.id)
id = idString.int ?? 0
title = try container.decode(String.self, forKey: CodingKeys.title)
playbackURL = try container.decodeIfPresent(String.self, forKey: CodingKeys.playbackUrl) ?? ""
}
}
ViewModel.swift
let disposeBag = DisposeBag()
var searchText = BehaviorRelay(value: "")
private let provider = MoyaProvider<MyAPI>()
let jsonResponseKeyPath = "results"
public lazy var data: Driver<[Track]> = getData()
private func searchTracks(query: String) -> Observable<[Track]> {
let decoder = JSONDecoder()
return provider.rx.request(.search(query: query))
.filterSuccessfulStatusCodes()
.map([Track].self, atKeyPath: jsonResponseKeyPath, using: decoder, failsOnEmptyData: false)
.asObservable()
}
private func getTracksMetadata(tracks: Array<Track>) -> Observable<[Track]> {
let trackIds: String = tracks.map( { $0.id.description } ).joined(separator: ",")
let decoder = JSONDecoder()
return provider.rx.request(.getTracksMetadata(trackIds: trackIds))
.filterSuccessfulStatusCodes()
.map({ result -> [Track] in
})
.asObservable()
}
private func getData() -> Driver<[Track]> {
return self.searchText.asObservable()
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest(searchTracks)
.flatMapLatest(getTracksMetadata)
.asDriver(onErrorJustReturn: )
}
The JSON result for .search API call is structured like this:
{
"results": [
{
"id": "4912",
"trackid": 4912,
"artistid": 1,
"title": "Hello babe",
"artistname": "Some artist name",
"albumtitle": "The Best Of 1990-2000",
"duration": 113
},
{
...
}
]
}
The JSON result for .getTracksMetadata API call is structured like this:
[
{
"TrackID": "4912",
"Title": "Hello babe",
"Album": "The Best Of 1990-2000",
"Artists": [
{
"ArtistID": "1",
"ArtistName": "Some artist name"
}
],
"SomeOtherImportantMetadata1": "Something something 1",
"SomeOtherImportantMetadata2": "Something something 2",
"SomeOtherImportantMetadata3": "Something something 3"
},
{
...
}
]
ios swift rx-swift moya
add a comment |
I have a model called Track. It has a set of basic and a set of extended properties. List of tracks and their basic properties are fetched with a search API call, then I need to make another API call with those track IDs to fetch their extended properties.
The question is how to best combine the results of both API calls and populate the extended properties into the already created Track objects, and of course match them by ID (which unfortunately is a different property name in both calls' results). Note that there are many more keys returned in the real results sets - around 20-30 properties for each of the two calls.
Track.swift
struct Track: Decodable {
// MARK: - Basic properties
let id: Int
let title: String
// MARK: - Extended properties
let playbackURL: String
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "title"
case playbackUrl = "playbackUrl"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let idString = try container.decode(String.self, forKey: CodingKeys.id)
id = idString.int ?? 0
title = try container.decode(String.self, forKey: CodingKeys.title)
playbackURL = try container.decodeIfPresent(String.self, forKey: CodingKeys.playbackUrl) ?? ""
}
}
ViewModel.swift
let disposeBag = DisposeBag()
var searchText = BehaviorRelay(value: "")
private let provider = MoyaProvider<MyAPI>()
let jsonResponseKeyPath = "results"
public lazy var data: Driver<[Track]> = getData()
private func searchTracks(query: String) -> Observable<[Track]> {
let decoder = JSONDecoder()
return provider.rx.request(.search(query: query))
.filterSuccessfulStatusCodes()
.map([Track].self, atKeyPath: jsonResponseKeyPath, using: decoder, failsOnEmptyData: false)
.asObservable()
}
private func getTracksMetadata(tracks: Array<Track>) -> Observable<[Track]> {
let trackIds: String = tracks.map( { $0.id.description } ).joined(separator: ",")
let decoder = JSONDecoder()
return provider.rx.request(.getTracksMetadata(trackIds: trackIds))
.filterSuccessfulStatusCodes()
.map({ result -> [Track] in
})
.asObservable()
}
private func getData() -> Driver<[Track]> {
return self.searchText.asObservable()
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest(searchTracks)
.flatMapLatest(getTracksMetadata)
.asDriver(onErrorJustReturn: )
}
The JSON result for .search API call is structured like this:
{
"results": [
{
"id": "4912",
"trackid": 4912,
"artistid": 1,
"title": "Hello babe",
"artistname": "Some artist name",
"albumtitle": "The Best Of 1990-2000",
"duration": 113
},
{
...
}
]
}
The JSON result for .getTracksMetadata API call is structured like this:
[
{
"TrackID": "4912",
"Title": "Hello babe",
"Album": "The Best Of 1990-2000",
"Artists": [
{
"ArtistID": "1",
"ArtistName": "Some artist name"
}
],
"SomeOtherImportantMetadata1": "Something something 1",
"SomeOtherImportantMetadata2": "Something something 2",
"SomeOtherImportantMetadata3": "Something something 3"
},
{
...
}
]
ios swift rx-swift moya
This might be what you need reactivex.io/documentation/operators/combinelatest.html
– Bohdan Savych
Nov 16 '18 at 10:27
What does the JSON look like for the two calls?
– Daniel T.
Nov 16 '18 at 19:33
JSON results added to original question. The result set is much simplified, the result normally contains 20-30 properties for each object.
– Blackbeard
Nov 19 '18 at 10:17
add a comment |
I have a model called Track. It has a set of basic and a set of extended properties. List of tracks and their basic properties are fetched with a search API call, then I need to make another API call with those track IDs to fetch their extended properties.
The question is how to best combine the results of both API calls and populate the extended properties into the already created Track objects, and of course match them by ID (which unfortunately is a different property name in both calls' results). Note that there are many more keys returned in the real results sets - around 20-30 properties for each of the two calls.
Track.swift
struct Track: Decodable {
// MARK: - Basic properties
let id: Int
let title: String
// MARK: - Extended properties
let playbackURL: String
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "title"
case playbackUrl = "playbackUrl"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let idString = try container.decode(String.self, forKey: CodingKeys.id)
id = idString.int ?? 0
title = try container.decode(String.self, forKey: CodingKeys.title)
playbackURL = try container.decodeIfPresent(String.self, forKey: CodingKeys.playbackUrl) ?? ""
}
}
ViewModel.swift
let disposeBag = DisposeBag()
var searchText = BehaviorRelay(value: "")
private let provider = MoyaProvider<MyAPI>()
let jsonResponseKeyPath = "results"
public lazy var data: Driver<[Track]> = getData()
private func searchTracks(query: String) -> Observable<[Track]> {
let decoder = JSONDecoder()
return provider.rx.request(.search(query: query))
.filterSuccessfulStatusCodes()
.map([Track].self, atKeyPath: jsonResponseKeyPath, using: decoder, failsOnEmptyData: false)
.asObservable()
}
private func getTracksMetadata(tracks: Array<Track>) -> Observable<[Track]> {
let trackIds: String = tracks.map( { $0.id.description } ).joined(separator: ",")
let decoder = JSONDecoder()
return provider.rx.request(.getTracksMetadata(trackIds: trackIds))
.filterSuccessfulStatusCodes()
.map({ result -> [Track] in
})
.asObservable()
}
private func getData() -> Driver<[Track]> {
return self.searchText.asObservable()
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest(searchTracks)
.flatMapLatest(getTracksMetadata)
.asDriver(onErrorJustReturn: )
}
The JSON result for .search API call is structured like this:
{
"results": [
{
"id": "4912",
"trackid": 4912,
"artistid": 1,
"title": "Hello babe",
"artistname": "Some artist name",
"albumtitle": "The Best Of 1990-2000",
"duration": 113
},
{
...
}
]
}
The JSON result for .getTracksMetadata API call is structured like this:
[
{
"TrackID": "4912",
"Title": "Hello babe",
"Album": "The Best Of 1990-2000",
"Artists": [
{
"ArtistID": "1",
"ArtistName": "Some artist name"
}
],
"SomeOtherImportantMetadata1": "Something something 1",
"SomeOtherImportantMetadata2": "Something something 2",
"SomeOtherImportantMetadata3": "Something something 3"
},
{
...
}
]
ios swift rx-swift moya
I have a model called Track. It has a set of basic and a set of extended properties. List of tracks and their basic properties are fetched with a search API call, then I need to make another API call with those track IDs to fetch their extended properties.
The question is how to best combine the results of both API calls and populate the extended properties into the already created Track objects, and of course match them by ID (which unfortunately is a different property name in both calls' results). Note that there are many more keys returned in the real results sets - around 20-30 properties for each of the two calls.
Track.swift
struct Track: Decodable {
// MARK: - Basic properties
let id: Int
let title: String
// MARK: - Extended properties
let playbackURL: String
enum CodingKeys: String, CodingKey {
case id = "id"
case title = "title"
case playbackUrl = "playbackUrl"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let idString = try container.decode(String.self, forKey: CodingKeys.id)
id = idString.int ?? 0
title = try container.decode(String.self, forKey: CodingKeys.title)
playbackURL = try container.decodeIfPresent(String.self, forKey: CodingKeys.playbackUrl) ?? ""
}
}
ViewModel.swift
let disposeBag = DisposeBag()
var searchText = BehaviorRelay(value: "")
private let provider = MoyaProvider<MyAPI>()
let jsonResponseKeyPath = "results"
public lazy var data: Driver<[Track]> = getData()
private func searchTracks(query: String) -> Observable<[Track]> {
let decoder = JSONDecoder()
return provider.rx.request(.search(query: query))
.filterSuccessfulStatusCodes()
.map([Track].self, atKeyPath: jsonResponseKeyPath, using: decoder, failsOnEmptyData: false)
.asObservable()
}
private func getTracksMetadata(tracks: Array<Track>) -> Observable<[Track]> {
let trackIds: String = tracks.map( { $0.id.description } ).joined(separator: ",")
let decoder = JSONDecoder()
return provider.rx.request(.getTracksMetadata(trackIds: trackIds))
.filterSuccessfulStatusCodes()
.map({ result -> [Track] in
})
.asObservable()
}
private func getData() -> Driver<[Track]> {
return self.searchText.asObservable()
.throttle(0.3, scheduler: MainScheduler.instance)
.distinctUntilChanged()
.flatMapLatest(searchTracks)
.flatMapLatest(getTracksMetadata)
.asDriver(onErrorJustReturn: )
}
The JSON result for .search API call is structured like this:
{
"results": [
{
"id": "4912",
"trackid": 4912,
"artistid": 1,
"title": "Hello babe",
"artistname": "Some artist name",
"albumtitle": "The Best Of 1990-2000",
"duration": 113
},
{
...
}
]
}
The JSON result for .getTracksMetadata API call is structured like this:
[
{
"TrackID": "4912",
"Title": "Hello babe",
"Album": "The Best Of 1990-2000",
"Artists": [
{
"ArtistID": "1",
"ArtistName": "Some artist name"
}
],
"SomeOtherImportantMetadata1": "Something something 1",
"SomeOtherImportantMetadata2": "Something something 2",
"SomeOtherImportantMetadata3": "Something something 3"
},
{
...
}
]
ios swift rx-swift moya
ios swift rx-swift moya
edited Nov 19 '18 at 10:17
Blackbeard
asked Nov 16 '18 at 9:19
BlackbeardBlackbeard
3191417
3191417
This might be what you need reactivex.io/documentation/operators/combinelatest.html
– Bohdan Savych
Nov 16 '18 at 10:27
What does the JSON look like for the two calls?
– Daniel T.
Nov 16 '18 at 19:33
JSON results added to original question. The result set is much simplified, the result normally contains 20-30 properties for each object.
– Blackbeard
Nov 19 '18 at 10:17
add a comment |
This might be what you need reactivex.io/documentation/operators/combinelatest.html
– Bohdan Savych
Nov 16 '18 at 10:27
What does the JSON look like for the two calls?
– Daniel T.
Nov 16 '18 at 19:33
JSON results added to original question. The result set is much simplified, the result normally contains 20-30 properties for each object.
– Blackbeard
Nov 19 '18 at 10:17
This might be what you need reactivex.io/documentation/operators/combinelatest.html
– Bohdan Savych
Nov 16 '18 at 10:27
This might be what you need reactivex.io/documentation/operators/combinelatest.html
– Bohdan Savych
Nov 16 '18 at 10:27
What does the JSON look like for the two calls?
– Daniel T.
Nov 16 '18 at 19:33
What does the JSON look like for the two calls?
– Daniel T.
Nov 16 '18 at 19:33
JSON results added to original question. The result set is much simplified, the result normally contains 20-30 properties for each object.
– Blackbeard
Nov 19 '18 at 10:17
JSON results added to original question. The result set is much simplified, the result normally contains 20-30 properties for each object.
– Blackbeard
Nov 19 '18 at 10:17
add a comment |
1 Answer
1
active
oldest
votes
The solution here is a two phase approach. First you should define two different structs for the two network calls and a third struct for the combined result. Let's say you go with:
struct TrackBasic {
let id: Int
let title: String
}
struct TrackMetadata {
let id: Int // or whatever it's called.
let playbackURL: String
}
struct Track {
let id: Int
let title: String
let playbackURL: String
}
And define your functions like so:
func searchTracks(query: String) -> Observable<[TrackBasic]>
func getTracksMetadata(tracks: [Int]) -> Observable<[TrackMetadata]>
Now you can make the two calls and wrap the data from the two separate endpoints into the combined struct:
searchText
.flatMapLatest { searchTracks(query: $0) }
.flatMapLatest { basicTracks in
Observable.combineLatest(Observable.just(basicTracks), getTracksMetadata(tracks: basicTracks.map { $0.id }))
}
.map { zip($0.0, $0.1) }
.map { $0.map { Track(id: $0.0.id, title: $0.0.title, playbackURL: $0.1.playbackURL) } }
The above assumes that the track metadata comes in the same order that it was requested in. If that is not the case then the last map will have to be more complex.
The key here is to useflatMap
,combineLatest
, andjust
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…
– Daniel T.
Nov 16 '18 at 19:56
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.
– Blackbeard
Nov 19 '18 at 10:22
1
That's fine, just sort them before passing to the zip.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two propertiesstruct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.
– Daniel T.
Nov 19 '18 at 13:08
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53334778%2fcombine-the-results-of-2-api-calls-fetching-different-properties-for-the-same-ob%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The solution here is a two phase approach. First you should define two different structs for the two network calls and a third struct for the combined result. Let's say you go with:
struct TrackBasic {
let id: Int
let title: String
}
struct TrackMetadata {
let id: Int // or whatever it's called.
let playbackURL: String
}
struct Track {
let id: Int
let title: String
let playbackURL: String
}
And define your functions like so:
func searchTracks(query: String) -> Observable<[TrackBasic]>
func getTracksMetadata(tracks: [Int]) -> Observable<[TrackMetadata]>
Now you can make the two calls and wrap the data from the two separate endpoints into the combined struct:
searchText
.flatMapLatest { searchTracks(query: $0) }
.flatMapLatest { basicTracks in
Observable.combineLatest(Observable.just(basicTracks), getTracksMetadata(tracks: basicTracks.map { $0.id }))
}
.map { zip($0.0, $0.1) }
.map { $0.map { Track(id: $0.0.id, title: $0.0.title, playbackURL: $0.1.playbackURL) } }
The above assumes that the track metadata comes in the same order that it was requested in. If that is not the case then the last map will have to be more complex.
The key here is to useflatMap
,combineLatest
, andjust
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…
– Daniel T.
Nov 16 '18 at 19:56
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.
– Blackbeard
Nov 19 '18 at 10:22
1
That's fine, just sort them before passing to the zip.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two propertiesstruct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.
– Daniel T.
Nov 19 '18 at 13:08
add a comment |
The solution here is a two phase approach. First you should define two different structs for the two network calls and a third struct for the combined result. Let's say you go with:
struct TrackBasic {
let id: Int
let title: String
}
struct TrackMetadata {
let id: Int // or whatever it's called.
let playbackURL: String
}
struct Track {
let id: Int
let title: String
let playbackURL: String
}
And define your functions like so:
func searchTracks(query: String) -> Observable<[TrackBasic]>
func getTracksMetadata(tracks: [Int]) -> Observable<[TrackMetadata]>
Now you can make the two calls and wrap the data from the two separate endpoints into the combined struct:
searchText
.flatMapLatest { searchTracks(query: $0) }
.flatMapLatest { basicTracks in
Observable.combineLatest(Observable.just(basicTracks), getTracksMetadata(tracks: basicTracks.map { $0.id }))
}
.map { zip($0.0, $0.1) }
.map { $0.map { Track(id: $0.0.id, title: $0.0.title, playbackURL: $0.1.playbackURL) } }
The above assumes that the track metadata comes in the same order that it was requested in. If that is not the case then the last map will have to be more complex.
The key here is to useflatMap
,combineLatest
, andjust
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…
– Daniel T.
Nov 16 '18 at 19:56
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.
– Blackbeard
Nov 19 '18 at 10:22
1
That's fine, just sort them before passing to the zip.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two propertiesstruct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.
– Daniel T.
Nov 19 '18 at 13:08
add a comment |
The solution here is a two phase approach. First you should define two different structs for the two network calls and a third struct for the combined result. Let's say you go with:
struct TrackBasic {
let id: Int
let title: String
}
struct TrackMetadata {
let id: Int // or whatever it's called.
let playbackURL: String
}
struct Track {
let id: Int
let title: String
let playbackURL: String
}
And define your functions like so:
func searchTracks(query: String) -> Observable<[TrackBasic]>
func getTracksMetadata(tracks: [Int]) -> Observable<[TrackMetadata]>
Now you can make the two calls and wrap the data from the two separate endpoints into the combined struct:
searchText
.flatMapLatest { searchTracks(query: $0) }
.flatMapLatest { basicTracks in
Observable.combineLatest(Observable.just(basicTracks), getTracksMetadata(tracks: basicTracks.map { $0.id }))
}
.map { zip($0.0, $0.1) }
.map { $0.map { Track(id: $0.0.id, title: $0.0.title, playbackURL: $0.1.playbackURL) } }
The above assumes that the track metadata comes in the same order that it was requested in. If that is not the case then the last map will have to be more complex.
The solution here is a two phase approach. First you should define two different structs for the two network calls and a third struct for the combined result. Let's say you go with:
struct TrackBasic {
let id: Int
let title: String
}
struct TrackMetadata {
let id: Int // or whatever it's called.
let playbackURL: String
}
struct Track {
let id: Int
let title: String
let playbackURL: String
}
And define your functions like so:
func searchTracks(query: String) -> Observable<[TrackBasic]>
func getTracksMetadata(tracks: [Int]) -> Observable<[TrackMetadata]>
Now you can make the two calls and wrap the data from the two separate endpoints into the combined struct:
searchText
.flatMapLatest { searchTracks(query: $0) }
.flatMapLatest { basicTracks in
Observable.combineLatest(Observable.just(basicTracks), getTracksMetadata(tracks: basicTracks.map { $0.id }))
}
.map { zip($0.0, $0.1) }
.map { $0.map { Track(id: $0.0.id, title: $0.0.title, playbackURL: $0.1.playbackURL) } }
The above assumes that the track metadata comes in the same order that it was requested in. If that is not the case then the last map will have to be more complex.
edited Nov 16 '18 at 19:58
answered Nov 16 '18 at 19:53
Daniel T.Daniel T.
14.5k22734
14.5k22734
The key here is to useflatMap
,combineLatest
, andjust
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…
– Daniel T.
Nov 16 '18 at 19:56
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.
– Blackbeard
Nov 19 '18 at 10:22
1
That's fine, just sort them before passing to the zip.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two propertiesstruct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.
– Daniel T.
Nov 19 '18 at 13:08
add a comment |
The key here is to useflatMap
,combineLatest
, andjust
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…
– Daniel T.
Nov 16 '18 at 19:56
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.
– Blackbeard
Nov 19 '18 at 10:22
1
That's fine, just sort them before passing to the zip.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two propertiesstruct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.
– Daniel T.
Nov 19 '18 at 13:08
The key here is to use
flatMap
, combineLatest
, and just
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…– Daniel T.
Nov 16 '18 at 19:56
The key here is to use
flatMap
, combineLatest
, and just
to carry over the old data along with the new data. This and other interesting ways of combining observables can be found here: medium.com/@danielt1263/…– Daniel T.
Nov 16 '18 at 19:56
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties
(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.– Blackbeard
Nov 19 '18 at 10:22
The thing is that I cannot assume that the order is the same for both API calls. Also I need to compare single results in each result array by different properties
(if obj.id == otherobj.trackid)
- see the newly added JSON result set in original question. And third, there are many many properties in the actual production result set, so describing each property in the Track() initializer would be cumbersome.– Blackbeard
Nov 19 '18 at 10:22
1
1
That's fine, just sort them before passing to the zip
.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two properties struct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.– Daniel T.
Nov 19 '18 at 13:08
That's fine, just sort them before passing to the zip
.map { zip($0.sorted(by: { $0.id < $1.id }), $1.sorted(by: { $0.trackID < $1.trackID })) }
and make the Track struct simpler by giving it only two properties struct Track { let basic: TrackBasic; let meta: TrackMetadata }
. The basic advice is still the same, use flatMap, combineLatest and just.– Daniel T.
Nov 19 '18 at 13:08
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53334778%2fcombine-the-results-of-2-api-calls-fetching-different-properties-for-the-same-ob%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
This might be what you need reactivex.io/documentation/operators/combinelatest.html
– Bohdan Savych
Nov 16 '18 at 10:27
What does the JSON look like for the two calls?
– Daniel T.
Nov 16 '18 at 19:33
JSON results added to original question. The result set is much simplified, the result normally contains 20-30 properties for each object.
– Blackbeard
Nov 19 '18 at 10:17