Лучший способ найти индекс элемента массива? - PullRequest
2 голосов
/ 15 марта 2019

Я относительно новичок в Swift и программировании в целом.

Я работаю над проектом с двумерным массивом для представления сетки, в которой я храню пользовательские объекты:

class Grid {
    var element: [[GridElement]]
}

Мне часто нужно получить доступ к элементам по их координатам. Пример:

let gridElement = grid.element[coord.x][coord.y]

Но Мне также нужно часто возвращать координаты из данного gridElement (его индексы в массиве 2d). В результате получается вложенный цикл, который не является самым быстрым и элегантным решением:

func getCoord(_ gridElement: GridElement) -> Coord {
    for x in 0..<xSize {
        for y in 0..<ySize {
            if element[x][y] == gridElement { return Coord(x: x, y: y) }
        }
    }
    return Coord(x: -1, y: -1)
}

Поскольку я хочу, чтобы это работало гладко с большими сетками, это не похоже на решение. Я упускаю что-то простое?

Я рассматриваю вопрос о сохранении координаты в самом объекте GridElement , но это также выглядит как плохой дизайн, потому что мне нужно постоянно обновлять его, когда GridElement меняет местоположение.

Это больше вопрос дизайна / архитектуры, чем поиск магической функции для решения моих проблем. Такое ощущение, что шаблон проектирования для двухсторонней ссылки должен быть установлен, но я не смог найти ответ. Любой совет?

Ответы [ 3 ]

0 голосов
/ 15 марта 2019

Я рассматриваю возможность сохранения координаты в самом объекте GridElement, но это также выглядит как плохой дизайн, потому что мне нужно будет постоянно обновлять его, когда GridElement меняет местоположение.

Я думаю, что-то вроде этого является жизнеспособным решением. Вместо того, чтобы хранить его внутри объекта элемента сетки, вы можете сохранить координаты в другом словаре:

private var gridElementPositionDict: [GridElement: Coord]

Как вы сказали, для этого потребуется постоянно устанавливать значения для gridElementPositionDict всякий раз, когда элемент сетки меняет положение. Однако вы можете использовать некоторые инкапсуляции, чтобы установить их только в одном месте.

Сначала создайте массив elements private и добавьте subscript, который принимает два параметра, в ваш класс Grid. Подстрочный индекс примет Coord для доступа к конкретному элементу сетки в определенной позиции. В установщике этого индекса вы можете изменить gridElementPositionDict для установки новых позиций GridElement.

Затем вы можете написать другой метод (или индекс), который принимает GridPosition, который возвращает свою позицию.

Вы также можете добавить такие методы, как swapElements(at:and:), changeElementPositon(_:to:) и т. Д. Все дело в инкапсуляции вашей структуры данных.

Вот пример кода:

class Grid<T: Hashable> {
    private var elements: [[T?]]
    private var elementsPositionDict: [T: Coord]

    init() {
        elements = [
            [nil, nil, nil, nil],
            [nil, nil, nil, nil],
            [nil, nil, nil, nil],
            [nil, nil, nil, nil],
        ]
        elementsPositionDict = [:]
    }

    subscript(_ coord: Coord) -> T? {
        get { return elements[coord.x][coord.y] }
        set {
            // this is the ONLY place you modify the dictionary
            if let oldValue = elements[coord.x][coord.y] {
                elementsPositionDict[oldValue] = nil
            }
            elements[coord.x][coord.y] = newValue
            if let v = newValue {
                elementsPositionDict[v] = coord
            }
        }
    }

    func coord(of element: T) -> Coord? {
        return elementsPositionDict[element]
    }
}
0 голосов
/ 15 марта 2019

Я думаю, что ваша getCoord функция должна быть такой:

func getCoord(_ gridElement: GridElement) -> Coord {
    for (i, row) in element.enumerated() {
        if let index = row.firstIndex(of: gridElement) {
            return Coord(x: i, y: index)
        }
    }
    return Coord(x: -1, y: -1)
}
0 голосов
/ 15 марта 2019

Поскольку у вас есть класс / структура Coord, почему бы не хранить ваши элементы в виде словаря Coord и GridElement вместо этого, чтобы избежать 2D-массива и сопоставления между Coord и индексами в массиве.Вот мое предложение, как реализовать Grid класс

class Grid {
    private var elements: [Coord: GridElement]

    init() {
        elements = [:]
    }

    func element(at coord: Coord) -> GridElement? {
       return elements[coord]
    }

    func coord(for gridElement: GridElement) -> Coord? {
        return elements.first { item in item.value == gridElement }?.key
    }
}

Обратите внимание, что Coord должен реализовывать Hashable, а также что я сделал словарь приватным, так как я думаю, что это гораздо лучший дизайн, если выполучить доступ к нему через функции

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...