Matriz rápida - Verifique se existe um índice


139

No Swift, existe alguma maneira de verificar se existe um índice em uma matriz sem que um erro fatal seja gerado?

Eu esperava poder fazer algo assim:

let arr: [String] = ["foo", "bar"]
let str: String? = arr[1]
if let str2 = arr[2] as String? {
    // this wouldn't run
    println(str2)
} else {
    // this would be run
}

Mas eu entendo

erro fatal: índice da matriz fora do intervalo

Respostas:


441

Uma maneira elegante em Swift:

let isIndexValid = array.indices.contains(index)

10
Na vida real, isso não deveria importar, mas a complexidade do tempo não seria muito melhor usar index < array.count?
funct7

2
Sinceramente, acho que o exemplo que você deu é um pouco artificial, pois nunca experimentei ter um valor de índice negativo, mas se esse realmente fosse o caso, duas verificações verdadeiras / falsas seriam ainda melhores: ie, em index >= 0 && index < array.countvez do pior caso ser n comparações.
funct7

13
Caso você se pergunte qual é a diferença de velocidade, eu a medi com as ferramentas do Xcode e é insignificante. gist.github.com/masonmark/a79bfa1204c957043687f4bdaef0c2ad
Mason

7
Apenas para acrescentar outro ponto sobre o motivo: se você aplicar a mesma lógica ao trabalhar em um ArraySlice, o primeiro índice não será 0, portanto, fazer index >= 0isso não será uma verificação suficientemente boa. .indicesem vez disso, funciona em qualquer caso.
precisa saber é o seguinte

2
Eu amo Swift por isso. Aqui está o meu caso de uso: criando uma estrutura JSON de merda para o serviço, então foi necessário fazer isso: "participante3": names.indices.contains (2)? nomes [2]: ""
Lee Probert

64

Extensão do tipo:

extension Collection {

    subscript(optional i: Index) -> Iterator.Element? {
        return self.indices.contains(i) ? self[i] : nil
    }

}

Usando isso, você obtém um valor opcional de volta ao adicionar a palavra-chave opcional ao seu índice, o que significa que seu programa não falha, mesmo que o índice esteja fora do intervalo. No seu exemplo:

let arr = ["foo", "bar"]
let str1 = arr[optional: 1] // --> str1 is now Optional("bar")
if let str2 = arr[optional: 2] {
    print(str2) // --> this still wouldn't run
} else {
    print("No string found at that index") // --> this would be printed
}

4
Resposta excelente 👏 O mais importante é legível ao usar o optionalparâmetro Obrigado!
Jakub

2
Awesomeness: D Salvou meu dia.
Codetard 22/09

2
Esta é bonito
JoeGalind

32

Basta verificar se o índice é menor que o tamanho da matriz:

if 2 < arr.count {
    ...
} else {
    ...
}

3
E se o tamanho da matriz for desconhecido?
Nathan McKaskle

8
@NathanMcKaskle A matriz sempre sabe quantos elementos ela contém, então o tamanho não pode ser desconhecido.
Antonio

16
Desculpe, eu não estava pensando lá. O café da manhã ainda estava entrando na corrente sanguínea.
Nathan McKaskle

4
@NathanMcKaskle não se preocupe ... às vezes isso acontece comigo, mesmo depois de vários cafés ;-)
Antonio

De certa forma, esta é a melhor resposta, porque é executada em O (1) em vez de O (n).
ScottyBlades

12

Adicione um pouco de extensão de açúcar:

extension Collection {
  subscript(safe index: Index) -> Iterator.Element? {
    guard indices.contains(index) else { return nil }
    return self[index]
  }
}

if let item = ["a", "b", "c", "d"][safe: 3] { print(item) }//Output: "d"
//or with guard:
guard let anotherItem = ["a", "b", "c", "d"][safe: 3] else {return}
print(anotherItem) // "d"

Aprimora a legibilidade ao fazer if letcodificação de estilo em conjunto com matrizes


2
Honestamente, esta é a maneira mais swifty fazê-lo com a legibilidade máxima e clareza
barndog

1
@barndog Ame também. Eu costumo adicionar isso como Sugar em qualquer projeto que eu inicie. Adicionado exemplo de guarda também. Agradecemos à rápida comunidade de folga por ter criado essa.
eonist

boa solução ... mas a saída imprimirá "d" no exemplo que você está dando ...
jayant rawat 15/02

Isso deve fazer parte do idioma Swift. A questão do índice fora da faixa não é menos problemática que os valores nulos. Eu até sugeriria usar sintaxe semelhante: talvez algo como: myArray [? 3]. Se o myArray for opcional, você receberá o myArray? [? 3]
Andy Weinstein

@ Andy Weinstein Você deve propor a apple apple
eonist

7

Você pode reescrever isso de uma maneira mais segura para verificar o tamanho da matriz e usar uma condicional ternária:

if let str2 = (arr.count > 2 ? arr[2] : nil) as String?

1
O que Antonio sugere é muito mais transparente (e eficiente). Onde o ternário seria apropriado é se você tivesse um valor prontamente disponível para usar e eliminar o se completamente, deixando apenas uma declaração let.
David Berry

1
@ David O que Antonio sugere requer duas ifinstruções em vez de uma ifno código original. Meu código substitui o segundo ifpor um operador condicional, permitindo que você mantenha um único em elsevez de forçar dois elseblocos separados .
dasblinkenlight

1
Não estou vendo duas declarações if no código dele, coloque let str2 = arr [1] para as elipses e pronto. Se você substituir a instrução OP if let por antonio if if e mover a atribuição para dentro (ou não, já que o único uso de str2 é imprimi-lo, não há necessidade alguma, basta colocar a desreferência em linha no println. que o operador ternário é apenas um if / else obscuro (para alguns :)). Se arr. count> 2, então arr [2] nunca pode ser nulo, então por que mapeá-lo para String? apenas para que você possa aplicar outra declaração if.
David Berry

@ David A totalidade ifda pergunta do OP vai acabar dentro do ramo "then" da resposta de Antonio, então haverá dois ifs aninhados . Estou vendo o código de OPs como um pequeno exemplo, portanto, suponho que ele ainda deseje um if. Concordo com você que, no exemplo dele, ifnão é necessário. Mas, novamente, a declaração inteira é inútil, porque o OP sabe que a matriz não tem comprimento suficiente e que nenhum de seus elementos é nil, portanto ele poderia remover o ife manter apenas seu elsebloco.
dasblinkenlight

Não, não seria. Antonio se substitui a declaração se do OP. Como o tipo de matriz é [String], você sabe que nunca pode conter nada, portanto, não é necessário verificar nada além do comprimento.
David Berry

7

Extensão Swift 4:

Para mim, eu prefiro o método.

// MARK: - Extension Collection

extension Collection {

    /// Get at index object
    ///
    /// - Parameter index: Index of object
    /// - Returns: Element at index or nil
    func get(at index: Index) -> Iterator.Element? {
        return self.indices.contains(index) ? self[index] : nil
    }
}

Obrigado a @Benno Kress


1

Afirmando se existe um índice de matriz:

Essa metodologia é ótima se você não deseja adicionar açúcar de extensão:

let arr = [1,2,3]
if let fourthItem = (3 < arr.count ?  arr[3] : nil ) {
     Swift.print("fourthItem:  \(fourthItem)")
}else if let thirdItem = (2 < arr.count ?  arr[2] : nil) {
     Swift.print("thirdItem:  \(thirdItem)")
}
//Output: thirdItem: 3

1
extension Array {
    func isValidIndex(_ index : Int) -> Bool {
        return index < self.count
    }
}

let array = ["a","b","c","d"]

func testArrayIndex(_ index : Int) {

    guard array.isValidIndex(index) else {
        print("Handle array index Out of bounds here")
        return
    }

}

É trabalho para mim lidar com indexOutOfBounds .


E se o índice for negativo? Você provavelmente deveria verificar isso também.
Frankie Simon
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.