2016-03-17 4 views
0

Когда у меня есть ссылка на элемент в массиве, я хотел бы найти другой ближайший к нему элемент, соответствующий определенным критериям (вперед или назад).Найти ближайший элемент в массиве, который соответствует критериям в Swift?

К примеру, у меня есть массив:

let items = [ 
    (a: "Item 1", b: "F", c: 3), 
    (a: "Item 2", b: "S", c: 5), 
    (a: "Item 3", b: "D", c: 7), 
    (a: "Item 4", b: "A", c: 9), 
    (a: "Item 5", b: "M", c: 11), 
    (a: "Item 6", b: "I", c: 13), 
    (a: "Item 7", b: "F", c: 15), 
    (a: "Item 8", b: "S", c: 17), 
    (a: "Item 9", b: "D", c: 19), 
    (a: "Item 10", b: "A", c: 21), 
    (a: "Item 11", b: "M", c: 23), 
    (a: "Item 12", b: "I", c: 13), 
    (a: "Item 13", b: "F", c: 15), 
    (a: "Item 14", b: "S", c: 17), 
    (a: "Item 15", b: "D", c: 19), 
    (a: "Item 16", b: "A", c: 21), 
    (a: "Item 17", b: "M", c: 23), 
    (a: "Item 18", b: "I", c: 13), 
    (a: "Item 19", b: "F", c: 15), 
    (a: "Item 20", b: "S", c: 17), 
    (a: "Item 21", b: "D", c: 19), 
    (a: "Item 22", b: "A", c: 21), 
    (a: "Item 23", b: "M", c: 23), 
    (a: "Item 24", b: "I", c: 13) 
] 

Теперь у меня есть item[7], как я могу найти ближайший пункт, который имеет b = "I"? Я могу думать только о нескольких вложенных циклах, но звучит беспорядочно и не очень хорошо работает. Также, имея в виду, я не хочу, чтобы при поиске находилась проблема out of range. Любые быстрые идеи о том, как справиться с этим?

ответ

1

Ниже приведено общее описание массива, которое должно выполнять то, что вы ищете. Она возвращает кортеж, содержащий индекс и значение ближайшего матча:

extension Array { 
    func closestMatch(index:Index, predicate:(Element)->Bool) -> (Int, Element)? { 

     if predicate(self[index]) { 
      return (index, self[index]) 
     } 

     var delta = 1 

     while(true) { 
      guard index + delta < count || index - delta >= 0 else { 
       return nil 
      } 

      if index + delta < count && predicate(self[index + delta]) { 
       return (index + delta, self[index + delta]) 
      } 

      if index - delta >= 0 && predicate(self[index - delta]) { 
       return (index - delta, self[index - delta]) 
      } 

      delta = delta + 1 
     } 
    } 
} 

print(items.closestMatch(7) { $0.1 == "I" }) 
1

Поддельный код:

int index = 7; 

int delta = 0; 
while (true) 
{ 
    delta = delta + 1; 

    if (index - delta >= 0 && matches(items[ index - delta]) { 

     // Found closest index on Left : index-delta 

     break; 
    } 

    if (index + delta < items.length && matches(items[ index + delta]) { 

     // Found closest index on Right: index + delta 

     break; 
    } 
} 

// Time complexity: O(N) 

Вы можете легко конвертировать поддельный код Swift код.

0

Эта функция должна работать:

func findClosestItem(index: Int, items: [(a: String, b: String, c: Int)]) -> (a: String, b: String, c: Int)? { 

    guard index < items.count && index > -1 else{ 
     return nil 
    } 
    var foundItem: (String, String, Int)? = nil 
    var closestBefore = Int(INT16_MAX) 
    for i in (index - 1).stride(to: 0, by: -1) { 
     if items[index].b == items[i].b { 
      closestBefore = index - i 
      foundItem = items[i] 
      break 
     } 
    } 

    var closestAfter = Int(INT16_MAX) 
    for i in index + 1 ..< items.count { 
     if items[index].b == items[i].b { 
      closestAfter = i - index 
      if closestAfter < closestBefore { 
       foundItem = items[i] 
      } 
      break 
     } 
    } 

    return foundItem 
} 

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

Он просто ищет назад, пока не найдет соответствующий элемент, затем ищет вперед, пока не найдет соответствующий элемент, и вернет тот, который ближе всего.

0

Простейшее будет:

let startIndex = 7 
if startIndex < items.count, 
    let nextIndex = items[startIndex+1..<items.endIndex].indexOf({ $0.b == items[startIndex].b }) { 
    print(items[nextIndex]) // => "("Item 14", "S", 17)\n" 
} 

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

+0

Творческий и лаконичный, но он ищет только вперед. – TruMan1

+0

И результат не в том, чего хочет OP. – Eendje

+0

После того, как я отправил сообщение, я понял, что неправильно понял, что спрашивал ОП. Я собираюсь оставить его в качестве примера краткой находки, которая может быть использована для создания целого решения. – ColGraff

1

В дополнение к другим решениям, "один лайнер":

let index = 7 
let searchString = "I" 

let result = items.enumerate() 
    .filter { $0.1.b == searchString } 
    .map { (abs(index - $0.0), $0.1) } 
    .minElement { $0.0 < $1.0 } 
    .map { $0.1 } 

print(result) // Optional(("Item 6", "I", 13)) 

На основании ответа Дэвид Берри:

extension Array { 
    func closestMatch(index: Index, predicate: (Element) -> Bool) -> Element? { 
     return enumerate().filter { predicate($0.1) }.map { (abs(index - $0.0), $0.1) }.minElement { $0.0 < $1.0 }.map { $0.1 } 
    } 
} 

print(items.closestMatch(7) { $0.1 == "I" }) // Optional(("Item 6", "I", 13)) 

Примечание: Производительность мудрый ответ Дэвида Берри лучше.

Смежные вопросы