2017-10-22 2 views
-1

두 개의 배열을 하나로 결합하는 데 어려움을 겪고 있습니다.
배열의 연결은 levels.Id = rooms.level과 같습니다.
모든 아이디어를 감사하십시오!스위프트 : 필터 결합, 사전 매핑

let levels = [ 
    ["Name":"Floor 1", "Order":0, "Id": 1], 
    ["Name":"Floor 2", "Order":1, "Id": 2], 
    ["Name":"Outdoor", "Order":2, "Id": 3]] 
let rooms = [ 
    ["Name":"Master bedroom", "Order":2, "Id": 3, "Level": 2], 
    ["Name":"Guest bedroom", "Order":1, "Id": 4, "Level": 2], 
    ["Name":"Kitchen", "Order":0, "Id": 1, "Level": 1], 
    ["Name":"Boiler", "Order":0, "Id": 2, "Level": 3]] 

그리고 가정 및 결과 이렇게 될하려면 "주문"값으로 분류 두 배열에서 :
그래서, 여기에 사전의 제 2 개 배열은

["Floor 1" : ["Kitchen"], 
"Floor 2" : ["Guest bedroom", "Master bedroom"], 
"Outdoor" : ["Boiler"]] 

이 내가 만든 방법입니다 이 배열은 그러나 그것은 추한 외모 :

func replaceIdWithName(id : Int)->String { 
    return rooms.first(where: { $0["Id"] as? Int == id })!["Name"] as! String 
} 
func getLevelId(id : Int)->String { 
    return (levels.first(where: { $0["Id"] as? Int == rooms.first(where: { $0["Id"] as? Int == id })?["Level"] as? Int })?["Name"]) as! String 
} 
var arr : [String : [Any]] = Dictionary(grouping: rooms.map { $0["Id"] as! Int }, by: { getLevelId(id: $0) }) 
arr.forEach{ arr[$0.0] = $0.1.map { replaceIdWithName(id: $0 as! Int) } } 

그리고

, 나는이 FUNC 모두에 의해 배열을 정렬했습니다 전에 :

func sortLevels(p1:[String:Any], p2: [String:Any])->Bool { 
    let i = p1["Order"] as? Int 
    let j = p2["Order"] as? Int 
    if i != nil && j != nil { 
     return i! < j! 
    } else { 
     return false 
    } 
} 
+0

를 시도? 이 사이트는 "나에게이 숙제를 지정하십시오."장소가 아닙니다. –

+0

나는 그들이 필요로하는 정확히 방식으로 일하고 있지는 않지만 수백 가지 옵션을 시도했다. 누군가 나를 위해 코드를 작성할 의도가 없었습니다. 하지만 그것은 내 첫 게시물 여기에 나는 내가 바보 같은 시도를 게시 할 의무가있어 놓칠 수 ... – Annah

답변

0

사전 배열은 직접적으로 캐스팅하는 악몽이 될 것입니다. 그러나 어쨌든 그것을하고 싶다면 Swift 4의 새로운 사전 초기화 프로그램이 도움이 될 수 있습니다.

let levelNames = [Int:String](uniqueKeysWithValues:levels.map{($0["Id"]! as! Int, $0["Name"] as! String)}) 
let roomLevels = rooms.map{(levelNames[$0["Level"]! as! Int]! , [$0["Name"]! as! String])} 
let levelRooms = [String:[String]](roomLevels, uniquingKeysWith:+) 

print(levelRooms) 
// ["Floor 1": ["Kitchen"], "Floor 2": ["Master bedroom", "Guest bedroom"], "Outdoor": ["Boiler"]] 

:

다음은 예입니다.

levelNames 변수는 레벨 ID에서 레벨 이름을 얻기위한 색인 역할을합니다.

roomLevels 변수는 levelNames 변수를 사용하여 얻은 각각의 레벨 이름을 가진 방 이름의 배열입니다. 다음 단계를보다 쉽게하기 위해 방 이름이 단일 요소 배열로 반환됩니다.

levelRooms는 동일한 키 (레벨 이름)가있는 항목의 값 (회의실 이름)을 결합하는 uniquingKeysWith 옵션으로 초기화 된 최종 사전입니다. 방 이름이 단일 요소 배열로 반환 되었기 때문에 결합 함수에 + 연산자를 사용하면 레벨 이름에 대해 다중 요소 배열에 배열을 추가 할 수 있습니다.

[EDIT] 키 이름으로 값에 액세스하는 함수를 작성하여 사전의 배열을 데이터 구조로 사용하면서 대부분의 유형 변환을 숨길 수 있습니다.

struct Level 
{ 
    static func Id(_ dict:[String:Any]) -> Int { return dict["Id"] as? Int ?? 0 } 
    static func Name(_ dict:[String:Any]) -> String { return dict["Name"] as? String ?? ""} 
    static func Order(_ dict:[String:Any]) -> Int { return dict["Order"] as? Int ?? 0 } 
} 

struct Room 
{ 
    static func Id(_ dict:[String:Any]) -> Int { return dict["Id"] as? Int ?? 0 } 
    static func Name(_ dict:[String:Any]) -> String { return dict["Name"] as? String ?? ""} 
    static func Level(_ dict:[String:Any]) -> Int { return dict["Level"] as? Int ?? 0 } 
    static func Order(_ dict:[String:Any]) -> Int { return dict["Order"] as? Int ?? 0 } 
} 

위의 솔루션은 다음 훨씬 더 읽을 것입니다 그리고 당신은 모든 코드를 통해 문자열 리터럴에서 키 이름을 반복하지의 추가 혜택을받을

당신은 무엇을
let levelNames = [Int:String](uniqueKeysWithValues:levels.map{(Level.Id($0), Level.Name($0))}) 
let roomLevels = rooms.map{(levelNames[Room.Level($0)]! , [Room.Name($0)])} 
let levelRooms = [String:[String]](roomLevels, uniquingKeysWith:+) 
+0

놀랍습니다. 두 개의 일반 배열을 처리하는 데 많은 코드가 필요하다고 상상할 수는 없지만 여러분이 쓴 것은 저에게 큰 시작을주었습니다. 정말 고맙습니다. – Annah

0
struct Level { 
    let name: String 
    let order: Int 
    let id: Int 
} 

struct Room { 
    let name: String 
    let order: Int 
    let id: Int 
    let level: Int 
} 

먼저는

let mappedLevels = levels.flatMap { level -> Level? in 
    guard let name = level["Name"] as? String, let id = level["Id"] as? Int, let order = level["Order"] as? Int else { return nil } 

    return Level(name: name, order: order, id: id) 
} 

이 하나 levelIdMappings

let levelIdMappings = mappedLevels.reduce(Dictionary<Int, Level>()) { (result, level) -> Dictionary<Int, Level> in 
    var mutableResult = result 
    mutableResult[level.id] = level 

    return mutableResult 
} 

실내지도를 또한

let mappedRooms = rooms.flatMap { room -> Room? in 
    guard let name = room["Name"] as? String, let id = room["Id"] as? Int, let order = room["Order"] as? Int, let level = room["Level"] as? Int else { return nil } 

    return Room(name: name, order: order, id: id, level: level) 
} 
을 쉽게 일을 나중에 사용됩니다 쉽게 작업을 위해 일부 매핑을 할 필요가

n

let finalResult = levelRoomsMapping.reduce(Dictionary<String,[String]>()) { (dictionary, next) -> Dictionary<String,[String]> in 
    guard let key = levelIdMappings[next.key]?.name else { return dictionary } 

    var mutableDictionary = dictionary 
    mutableDictionary[key] = next.value.map { 
     $0.name 
    } 

    return mutableDictionary 
} 

당신은 붙여 넣을 수 있습니다 흐름에 관심이 만 레벨을 필터링하고 levelRoomsMapping을 채우기 최종 사전을

var levelRoomsMapping = Dictionary<Int, [Room]>() 

mappedRooms.filter { levelIdMappings.keys.contains($0.level) }.forEach { 
    if var rooms = levelRoomsMapping[$0.level] { 
     rooms.append($0) 
     levelRoomsMapping[$0.level] = rooms 
    } else { 
     levelRoomsMapping[$0.level] = [$0] 
    } 
} 

을 (나는이 감소와 함께 할 수있는) 지금 당신이 필요한 정보를 필터링하고, 생성 더 나은 통찰력을 얻으려는 것이 무엇이든간에 놀이터 및 인쇄물로 인쇄하십시오.

+0

물론 짧은 방법으로 할 수 있지만 덜 읽을 수있을 것 같아요 – Luzo

+0

거대한 감사합니다! 나는 그것을 아래에 보여줄 것이지만 그것은보기 흉한 것처럼 일했다. :) – Annah

+0

전혀, 그것을 분류하는 것을 잊었다 (그것을 발견하지 못했다). 어쨌든 배열을 구조체에 매핑하면 더 예쁜 코드를 얻을 수 있습니다. 또한 키 변경은 한 곳에서만 변경해야하므로 적응이 더 쉽습니다. 가능하면 [String]을 사용할 수 있다면 Any를 사용하지 마십시오. – Luzo