sort an array with Custom Objects swift 4 - arrays

public class OfferObjectModel: JSONJoy {
let rank : Int?
}
offersList.sort(by: { $0.rank < $1.rank }) -->Binary operator '<' cannot be applied to two 'Int?' operands
offersList is an array of OfferObjectModel

You can do something like this:
a.sort { (first, second) -> Bool in
guard let firstRank = first.rank else { return true }
guard let secondRank = second.rank else { return false }
return firstRank < secondRank
}
So this code block will make sure your nil ranks will be first and then sorted by rank value. And if change this part:
guard let firstRank = first.rank else { return false }
guard let secondRank = second.rank else { return true }
Nil ranks will be at the end.
And please make sure that your array is mutable.

Related

Initialise an array of non-optional objects with a given size n where the object's initialiser may fail

Long title but I think that summarises my problem best.
Given an integer n, I'd like to initialise an array with at most n objects.
The issue is that the objects in the array must not be nil, however, the constructor for the object type may fail (i.e. return nil).
There's two ways I came up with to solve that problem and I think there are more - is there a common, good-practice, well-known or better solution than the two below?
struct MyClass {
init?() {
// ...
if (/* cond */) { return nil }
}
}
// ...
// 1. solution
struct A {
let arr: [MyClass]
let n = 6
init?() {
let arr = (0..<n).map { _ in MyClass() }
guard arr.allSatisfy({ $0 != nil }) else { return nil }
self.arr = arr as! [MyClass] // At this point we can safely force-cast
}
}
// 2. solution
struct A {
let arr: [MyClass]
let n = 6
init() {
self.arr = (0..<n).map { _ in MyClass() }.reduce(into: []) { segments, segment in
if let segment = segment {
segments.append(segment)
}
}
}
}
You need compactMap.
self.arr = (0..<n).compactMap { _ in MyClass() }
compactMap takes a closure that returns an optional, and returns an array with all the nils filtered out. This should be exactly what you want, if I understood correctly.

Hash Tables: Ransom Note - Hacker Rank in Swift Timed Out

My code is alright but keeping getting time out on some test cases, any tips to improve this? My guess is the indexOf function taking too long.
func checkMagazine(magazine: [String], note: [String]) -> Void {
var mutableMag = magazine
if note.count > mutableMag.count {
print("No")
return
}
for word in note {
if let index = mutableMag.index(of: word) {
mutableMag.remove(at: index)
} else {
print("No")
return
}
}
print("Yes") }
Please find the challenge in this link: https://www.hackerrank.com/challenges/ctci-ransom-note/problem
One possible solution that passes all tests is using NSCountedSet for storing the words in the note and magazine and comparing the count of each word in note to the count of that word in magazine and if any of them is lower in magazine, making an early return and printing No.
I'd also suggest changing the function signature to return a Bool value even though the function prototype generated by hacker rank returns Void. It's better to make checkMagazine a pure function and not doing any I/O operations in it.
func checkMagazine(magazine: [String], note: [String]) -> Bool {
let magazineWords = NSCountedSet(array: magazine)
let noteWords = NSCountedSet(array: note)
for noteWord in noteWords {
if magazineWords.count(for: noteWord) < noteWords.count(for: noteWord) {
return false
}
}
return true
}
Then you just need to change the end of the generated code to the following:
let magazineWorks = checkMagazine(magazine: magazine, note: note)
if magazineWorks {
print("Yes")
} else {
print("No")
}
func checkMagazine(magazine: [String], note: [String]) -> Void {
var notesDictionary : [String : Int] = [:]
var magazineDictionary : [String : Int] = [:]
var canMakeRansom = true
for n in note {
var count = notesDictionary[n] ?? 0
count += 1
notesDictionary[n] = count
}
for n in magazine {
var count = magazineDictionary[n] ?? 0
count += 1
magazineDictionary[n] = count
}
for note in notesDictionary {
if note.value > magazineDictionary[note.key] ?? 0 {
canMakeRansom = false
}
}
print(canMakeRansom ? "Yes" : "No")
}
This is another way to solve this.
I think this does what NSCountedSet does by itself somehow.

Check if array contains something - Swift 4

I've found the .contains(Element) method pretty essential in my minimal experience writing Swift code, and have quickly realized that Apple changed it...
func contains(check:[[[Int]]], forElement: [[Int]]) -> Bool {
for element in check {
if areEqual(element, forElement) {
return true
}
}
return false
}
func areEqual(_ a:[[Int]], _ b:[[Int]]) -> Bool {
for i in 0..<a.count {
if a[i] != b[i] {
return false
}
}
return true
}
I've been messing with some large arrays, so I fixed my problem with that clunky function.
What happened?
How do you use the new way?
The example there is well over my head.
enum HTTPResponse {
case ok
case error(Int)
}
let lastThreeResponses: [HTTPResponse] = [.ok, .ok, .error(404)]
let hadError = lastThreeResponses.contains { element in
if case .error = element {
return true
} else {
return false
}
}
// 'hadError' == true
How about using this
let numbers = [1, 2, 3, 4]
let contains = numbers.contains(where: { $0 == 3 })
//contains == true
Or
let strings = ["A", "B", "C", "D"]
let contains = strings.contains(where: { $0 == "B" })
//contains == true
Even with other objects like NSColor
let colors: [NSColor] = [.red, .blue, .green]
contains = colors.contains(where: { $0 == .red })
//contains == true
That API just lets you pass a block where you can perform any checks you want. You can check whether the given element is equal to something, or whether its properties satisfy some constraints (such as in the example in the documentation). You should be able to perform a basic equality check like this:
func contains(check:[[[Int]]], forElement: [[Int]]) -> Bool {
return check.contains { element in
return areEqual(element, forElement)
}
}
func areEqual(_ a:[[Int]], _ b:[[Int]]) -> Bool {
for i in 0..<a.count {
if a[i] != b[i] {
return false
}
}
return true
}
The contains block iterates over every element and returns either true or false. In the end, a single true for any element will return true for the whole function.

How to append custom object to an array in Swift?

How to append custom class object to an array in Swift?
Below is my code, but it shows error.
Error:
"Cannot assign value of '()' to type [PhotoVC]"
Code:
var photoVCs = [PhotoVC]()
for index in 0 ..< photos.count {
if let vc = getPhotoController(index) {
photoVCs = photoVCs.append(vc)
}
}
func getPhotoController(index: Int) -> PhotoVC? {
if index < 0 || index == NSNotFound {
return nil
}
else if index == photos.count {
return nil
}
let PhotoVCID = "PhotoVCID"
if let storyboard = storyboard,
pageVC = storyboard.instantiateViewControllerWithIdentifier(PhotoVCID) as? PhotoVC {
pageVC.photoName = photos[index]
pageVC.photoIndex = index
return pageVC
}
return nil
}
I should be able to do it, but what's the problem?
append does not return anything. Remove the assignment:
photoVCs = photoVCs.append(vc) // wrong
photoVCs.append(vc) // ok

How to create a predicate to filter array of enums with associated values in Swift?

enum EnumType {
case WithString(String)
}
var enums = [EnumType]()
enums.append(EnumType.WithString("A"))
enums.append(EnumType.WithString("B"))
enums.append(EnumType.WithString("C"))
enums.append(EnumType.WithString("D"))
enums.append(EnumType.WithString("E"))
enums.append(EnumType.WithString("F"))
How to filter my enums array to find the one with associated value equal C. What predicate do I need to use?
The filter function can either be invoked as a global function over an array, or as an instance method (I prefer the later as it is more OO).
It accepts a closure with one parameter (the element being evaluated) that return a boolean (indicating whether the element matches the required condition).
Since it's a simple closure in a clear situation it's ok to use the abbreviated form.
I guess that other "With" cases would be added to your enum, so you could go with something like:
let filteredArray = enums.filter {
switch $0 {
case let .WithString(value):
return value == "C"
default:
return false
}
}
That should do the trick in your example.
As someone already mentioned, for Swift > 2.0 there's if case statement available:
enums.filter {
if case .WithString("C") = $0 {
return true
}
return false
}
But, it doesn't look nice, especially if you are going to repeat same logic again. What we can do here is to make EnumType conform to Equatable:
extension EnumType: Equatable {
}
func ==(lhs: EnumType, rhs: EnumType) -> Bool {
switch (lhs, rhs) {
case (.WithString(let lStr), .WithString(let rStr)):
return lStr == rStr
}
}
And now you can just:
enums.filter { $0 == .WithString("C") }
You could try adding a simple extension with a computed property to your enum and filter to that property:
extension EnumType {
var isC: Bool {
switch self {
case .WithString(let message): return message == "C"
default: return false
}
}
}
After this, you could simpy use the filtering as usual:
enums.filter { $0.isC }
var filteredArray = enums.filter { element in
switch element {
case EnumType.WithString(let string):
return string == "A"
default:
return false
}
}
This can probably be simplified with Swift 2.0 binding of assosciated values in if statements.
Inspired by #Jessy and SwiftLee, here is my solution:
// -----------------------
// CaseReflectable
// -----------------------
// designed for enums only
// (use it on other types not recommended)
protocol CaseReflectable {}
// default behaviors.
extension CaseReflectable {
/// case name
var caseName: String {
let mirror = Mirror(reflecting: self)
// enum cases:
// - normal case: no children
// - case with associated values: one child (with label)
guard let label = mirror.children.first?.label else {
return "\(self)" // normal case
}
// case with associated values
return label
}
/// associated values
var associatedValues: Any? {
// if no children, a normal case, no associated values.
guard let firstChild = Mirror(reflecting: self).children.first else {
return nil
}
return firstChild.value
}
}
// --------------------------
// custom operator ~=
// --------------------------
/// match enum cases with associated values, while disregarding the values themselves.
/// usage: `Enum.enumCase ~= instance`
func ~= <Enum: CaseReflectable, AssociatedValue>(
// an enum case (with associated values)
enumCase: (AssociatedValue) -> Enum, // enum case as function
// an instance of Enum
instance: Enum
) -> Bool
{
// if no associated values, `instance` can't be of `enumCase`
guard let values = instance.associatedValues else { return false }
// if associated values not of the same type, return false
guard values is AssociatedValue else { return false }
// create an instance from `enumCase` (as function)
let case2 = enumCase(values as! AssociatedValue)
// if same case name, return true
return case2.caseName == instance.caseName
}
// ------------
// Enum
// ------------
// enum with associated values
// (conforms to `CaseReflectable`)
enum Enum: CaseReflectable {
case int(Int)
case int2(Int)
case person(name: String, age: Int)
case str(String)
}
// ------------
// main
// ------------
let a: Enum = .int(3)
Enum.int ~= a // true
Enum.int2 ~= a // false
let joe = Enum.person(name: "joe", age: 8)
Enum.person ~= joe // true
Enum.int ~= joe // false
// array of enum cases
let items: [Enum] = [
.int(1), .str("hi"), .int(2)
]
// filter enum cases
let filtered = items.filter { Enum.int ~= $0 }
print(filtered) // [Enum.int(1), Enum.int(2)]
You can achieve something more reusable by implementing Equatable protocol:
enum EnumType {
case WithString(String)
}
extension EnumType: Equatable {
static func ==(lhs: EnumType, rhs: String) -> Bool {
switch lhs {
case .WithString(let value):
return value == rhs
}
}
}
EnumType.WithString("F") == "A" // false
EnumType.WithString("F") == "F" // true

Resources