1

I have an array of strings that I would like to map into different objects. I'm using map to transform the array to another, but it's very nested:

$favorites .map { articleIDs in articleIDs.compactMap { id in state.articles.first { $0.id == id } } } .assign(to: \Self.favorites, on: self) .store(in: &cancellable) 

Is there a shorthand to applying a transform to each individual item? I was originally trying to do this but it didn't work:

state.$favorites .mapEach { id in state.articles.first { $0.id == id } } .assign(to: \Self.favorites, on: self) .store(in: &cancellable) 
4
  • What type is value? Commented Nov 24, 2019 at 22:57
  • Value is an array of Article ID’s. I’ve updated the code for clarity. Commented Nov 24, 2019 at 23:05
  • If I'm reading this right, favorites is a [[String]], and you're mapping this into [[Article]], and then assign it back to favorites? (How is that possible? What type is favorites?) I usually try to demonstrate these using Just so I can show the entire flow. What's the input and output types? Commented Nov 25, 2019 at 1:24
  • I meant I'm trying to map favorites: [String] to [Article] in one flatting closure, but not sure if it's possible without a custom operator. Commented Nov 25, 2019 at 2:12

1 Answer 1

1

Here's one way to do it, you:

  1. convert a single array into a pipeline of individual elements with Publishers.Sequence
  2. Process the elements individually.
  3. Convert the elements back to an array with the collect operator.

Here's a contrived example that you can run in an Xcode playground:

import Combine import UIKit class MyStore: ObservableObject { @Published var favorites: [(id: Int, title: String)] = [] let articles = [ (id: 22, title: "foo"), (id: 5, title: "bar"), (id: 13, title: "baz"), ] var cancellable: Set<AnyCancellable> = [] func addFavorites(favorites: AnyPublisher<[Int], Never>) { let articles = self.articles favorites .flatMap(Publishers.Sequence.init) .compactMap { fav in articles.first { fav == $0.id }} .collect() .assign(to: \.favorites, on: self) .store(in: &cancellable) } } let store = MyStore() store.addFavorites(favorites: Just([22, 13]).eraseToAnyPublisher()) print(store.favorites) // => [(id: 22, title: "foo"), (id: 13, title: "baz")] 

... however I suspect this isn't actually what you want. A more elegant solution might be to create a bespoke compactMapEach operator. Here's what that looks like:

extension Publisher { public func compactMapEach<T, U>(_ transform: @escaping (T) -> U?) -> Publishers.Map<Self, [U]> where Output == [T] { return map { $0.compactMap(transform) } } } 
Sign up to request clarification or add additional context in comments.

1 Comment

Thx about the flatMap > compactMap > collect pipeline and suggestion of a custom compactMapEach operator!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.