جایگزینی flatMap با compactMap در نسخه ۴.۱ سوییفت

نسخه ۴.۱ زبان برنامه‌نویسی سوییفت بهمراه ایکس‌کد ۹.۳ (که در زمان نگارش این متن، هنوز بتاست) منتشر شده، و تغییرات جدیدی رو توی خود زبان و همینطور کتابخانه‌های استاندارد خودش داشته.

استفاده از flatMap که روی انواع ترتیبی داده‌ها (مثل آرایه‌ها) اعمال میشه، و تمام المان‌هایی که خروجی‌شون nil هست رو فیلتر می‌کنه، توی نسخه ۴.۱ با استفاده از متد compactMap انجام میشه؛ و این مورد استفاده flatMap توی نسخه ۴.۱ اصطلاحا deprecate شده.

خلاصه تغییر مورد بحث

برای حذف nilها از یه آرایه، از flatMap استفاده می‌کنین:

let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let valid = names.flatMap { $0 } 
// ["Tom", "Peter", "Harry"]

و ایکس‌کد ۹.۳ یه اخطار برای این نوع استفاده از flatMap بهتون نشون میده:

راه حل پیشنهادی خود ایکس‌کد هم جایگزینی flatMap با compactMap هست:

let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let valid = names.compactMap { $0 }
// ["Tom", "Peter", "Harry"]

هر زمان که از flatMap روی یه Sequence استفاده کنین، و اون flatMap یه مقدار Optional رو برگردونه، ایکس‌کد همین رفتار رو خواهد داشت. (خداییش هر کلمه فارسی بجای Optional استفاده می‌کردم، هیچکس نمیفهمید چی‌چی می‌گم ?)

پس توی مثال زیر هم، باز ایکس‌کد همون اخطار رو میده:

let words = ["53", "nine", "hello","0"]
let values = words.flatMap { Int($0) }

اینجا هم، با تغییر flatMap به compactMap اخطار ایکس‌کد رفع میشه:

let values = words.compactMap { Int($0) }
// Returns [Int] // [53, 0]


بیشتر بدانید!

? اول از همه، نسخه ۴.۱ سوییفت، تمام موارد استفاده از flatMap رو Deprecate نکرده؛ در واقع فقط یک روند استفاده رو اخطار میده.

توی سوییفت ۴.۰، سه رویه هست که می‌تونین از flatMap استفاده کنین:

  • استفاده از flatMap روی یه Sequence بهمراه یه closure که Sequence بر می‌گردونه:
Sequence.flatMap<S>(_ transform: (Element) -> S)  -> [S.Element] where S : Sequence

با استفاده از این رویه، از flatMap استفاده می‌کنین، و بعنوان مثال آرایه‌ای از آرایه رو، به یک آرایه (که شامل همه اعضای آرایه‌هاست) تبدیل می‌کنین:

let scores = [[5,2,7], [4,8], [9,1,3]]
let allScores = scores.flatMap { $0 }
// [5, 2, 7, 4, 8, 9, 1, 3]
let passMarks = scores.flatMap { $0.filter { $0 > 5} }
// [7, 8, 9]

سوییفت ۴.۱ کاری به کار این روند نداره! ?

  • استفاده از flatMap روی نوع داده Optional:

توی این روند استفاده، flatMap یه closure می‌گیره که اونم یه Optional بر می‌گردونه! (دقیقش رو نمی‌دونم که چه استفاده‌ای داره این کار! اگه کسی می‌دونه بگه)

Optional.flatMap<U>(_ transform: (Wrapped) -> U?) -> U?
let input: Int? = Int("8")
let passMark: Int? = input.flatMap { $0 > 5 ? $0 : nil}
// Optional(8)

بهرحال، سوییفت ۴.۱ به این یکی هم کاری نداره!


  • استفاده از flatMap روی یه Sequence و بهمراه یه Closure که می‌تونه Optional برگردونه:
Sequence.flatMap<U>(_ transform: (Element) -> U?) -> U?

این همون کاربردی هست که توی سوییفت ۴.۱ تغییر کرده؛ و برای همچین کاری، باید flatMap رو با compactMap جایگزین کنیم.

let names: [String?] = ["Tom", nil, "Peter", nil, "Harry"]
let counts = names.compactMap { $0?.count }
// [3, 5, 5]



ایده پیاده‌سازی compactMap احتمالا بخاطر گویایی بیشتر نام تابع هست؛ «با حذف المان‌های nil از یه آرایه، آرایه رو داریم فشرده می‌کنیم». حتی ممکنه در آینده، و در نسخه‌های بعدی سوییفت یه متد اضافه کنن به اسم compact که بدون اینکه خروجی داشته باشه، با حذف المان‌های nil، یه آرایه رو (در جا) فشرده می‌کنه.



? برای اطلاعات بیشتر، می‌تونین مستندات مربوط به این تغییرات رو در این آدرس ببینین.


? منبع مطلب: این مطلب رو بر اساس این پست نوشتم.