web-dev-qa-db-fra.com

Créer une séquence entre deux lettres

Je veux créer une séquence entre deux lettres, disons "b" et "f". Donc, la sortie est

"b" "c" "d" "e" "f"

Pour les chiffres, on peut faire

2:6 #which gives output as 
[1] 2 3 4 5 6

Y at-il un moyen facile de faire cela avec des lettres aussi?

J'ai traversé Génère une séquence de caractères de 'A' - 'Z' mais cela produit toutes les lettres et non une séquence entre des lettres spécifiques.

Ma solution actuelle est,

indx <- which(letters %in% c("b", "f")); 
letters[indx[1] : indx[2]]

#[1] "b" "c" "d" "e" "f"

Cela fonctionne, mais je suis curieux de savoir s’il existe un moyen facile de faire cela ou d’effectuer une fonction dans l’un des paquets que j’ai manqués.

Remarque: Je ne veux pas letters[2:6] comme je ne sais pas 2 et 6 à l’avance. Cela pourrait être entre deux lettres.

31
Ronak Shah

Ce serait une autre option de base R:

letters[(letters >= "b") & (letters <= "f")]
# [1] "b" "c" "d" "e" "f"
28
r.user.05apr

Vous pouvez créer votre propre fonction:

`%:%` <- function(l, r) {
    intToUtf8(seq(utf8ToInt(l), utf8ToInt(r)), multiple = TRUE)
}

Usage:

"b" %:% "f"
# [1] "b" "c" "d" "e" "f"

"f" %:% "b"
# [1] "f" "e" "d" "c" "b"

"A" %:% "D"
# [1] "A" "B" "C" "D"
17
Sven Hohenstein

Une autre option avec match, seq et do.call:

letters[do.call(seq, as.list(match(c("b","f"), letters)))]

qui donne:

[1] "b" "c" "d" "e" "f"

Faire en sorte que cela fonctionne avec les lettres minuscules et majuscules:

char_seq <- function(lets) {
  switch(all(grepl("[[:upper:]]", lets)) + 1L,
         letters[do.call(seq, as.list(match(lets, letters)))],
         LETTERS[do.call(seq, as.list(match(lets, LETTERS)))])
}

la sortie de ceci:

> char_seq(c("b","f"))
[1] "b" "c" "d" "e" "f"

> char_seq(c("B","F"))
[1] "B" "C" "D" "E" "F"

Cette fonction peut être étendue avec des contrôles sur l'exactitude de l'entrée:

char_seq <- function(lets) {
  g <- grepl("[[:upper:]]", lets)
  if(length(g) != 2) stop("Input is not of length 2")
  if(sum(g) == 1) stop("Input does not have all lower-case or all upper-case letters")
  switch(all(g) + 1L,
         letters[do.call(seq, as.list(match(lets, letters)))],
         LETTERS[do.call(seq, as.list(match(lets, LETTERS)))])
}

donnant lieu à des messages d'erreur corrects lorsque l'entrée n'est pas correcte:

> char_seq(c("B"))
Error in char_seq(c("B")) : Input is not of length 2

> char_seq(c("b","F"))
Error in char_seq(c("b", "F")) : 
  Input does not have all lower-case or all upper-case letters
12
Jaap

Jouer avec UTF, quelque chose comme:

intToUtf8(utf8ToInt("b"):utf8ToInt("f"), multiple = TRUE)
# [1] "b" "c" "d" "e" "f"
10
zx8754

Pourquoi pas?

letters[which(letters == 'b') : which(letters == 'f')]

Peut-être que l’utilisation des versions brutes des lettres, puis la conversion en caractères, pourraient être utilisées pour définir une fonction infixe analogue à ":"

 `%c:%` <- function(x,y) { strsplit( rawToChar(as.raw(
     seq(as.numeric(charToRaw(x)), as.numeric(charToRaw(y))))), "" )[[1]]}
>  'a' %c:% 'g'
[1] "a" "b" "c" "d" "e" "f" "g"

Je ne prétends certainement pas que cela répond à la demande de "moyen facile de faire cela" et je ne suis même pas sûr que ce serait plus efficace, mais cela introduit quelques fonctions potentiellement utiles.

8
42-

Je sais que c'est mal vu, mais voici une solution eval(parse(...))

LETTERS[eval(parse(text = paste(which(LETTERS %in% c('B', 'F')), collapse = ':')))]
#[1] "B" "C" "D" "E" "F"
6
Sotos

Tout d'abord: votre code

which(letters %in% c("b", "f"))

Est-ce une écriture valide mais compliquée

match(c('b', 'f'), letters)

(Pourquoi “alambiqué”? Parce que %in% est un wrapper autour de match pour un cas d’utilisation spécifique, qui transforme explicitement l’index numérique en une valeur logique, c’est-à-dire l’opération inverse de which.)

Ensuite, vous pouvez bien sûr utiliser le résultat et le convertir en une plage via idx[1L] : idx[2L] et il n’ya rien de mal à cela dans ce cas. Mais R a une manière idiomatique d’exprimer le concept d’appel d’une fonction utilisant un vecteur comme paramètre: do.call :

do.call(`:`, as.list(match(c('b', 'f'), letters)))

Ou équivalent:

do.call(seq, as.list(match(c('b', 'f'), letters)))

{purrr} nous permet de faire la même chose sans le as.list:

purrr::invoke(seq, match(c('b', 'f'), letters))

Et enfin, nous avons un sous-ensemble:

letters[purrr::invoke(seq, match(c('b', 'f'), letters))]
1
Konrad Rudolph