Je suis un débutant chez Ruby, je veux savoir si je peux utiliser une seule ligne pour faire le travail.
Prenez la 'recherche' de ce site par exemple. Lorsque l'utilisateur a tapé [Ruby] regex
, je peux utiliser le code suivant pour obtenir la balise et le mot clé
'[Ruby] regex' =~ /\[(.*?)\](.*)/
tag, keyword = $1, $2
Pouvons-nous l'écrire en une seule ligne?
METTRE À JOUR
Merci beaucoup! Puis-je rendre la chose plus difficile et plus intéressante, que l'entrée puisse contenir plus d'une balise, comme:
[Ruby] [regex] [Rails] one line
Est-il possible d'utiliser un code de ligne pour obtenir le tableau de balises et le mot-clé? J'ai essayé mais j'ai échoué.
Vous avez besoin de la méthode Regexp#match
. Si vous écrivez /\[(.*?)\](.*)/.match('[Ruby] regex')
, ceci retournera un objet MatchData
. Si nous appelons cet objet matches
, alors, entre autres choses:
matches[0]
renvoie la chaîne complète correspondante.matches[n]
renvoie le nième groupe de capture ($n
).matches.to_a
renvoie un tableau composé de matches[0]
à matches[N]
.matches.captures
renvoie un tableau composé uniquement du groupe de capture (matches[1]
à matches[N]
).matches.pre_match
renvoie tout ce qui précède la chaîne correspondante.matches.post_match
renvoie tout ce qui suit la chaîne correspondante.Il y a plus de méthodes, qui correspondent à d'autres variables spéciales, etc. vous pouvez consulter la documentation de MatchData
pour en savoir plus. Ainsi, dans ce cas précis, tout ce que vous devez écrire est
tag, keyword = /\[(.*?)\](.*)/.match('[Ruby] regex').captures
Edit 1: Très bien, pour votre tâche la plus difficile, vous allez plutôt vouloir utiliser la méthode String#scan
, qui @Theo used; Cependant, nous allons utiliser une expression régulière différente. Le code suivant devrait fonctionner:
# You could inline the regex, but comments would probably be Nice.
tag_and_text = / \[([^\]]*)\] # Match a bracket-delimited tag,
\s* # ignore spaces,
([^\[]*) /x # and match non-tag search text.
input = '[Ruby] [regex] [Rails] one line [foo] [bar] baz'
tags, texts = input.scan(tag_and_text).transpose
La input.scan(tag_and_text)
retournera une liste de paires tag-search-text:
[ ["Ruby", ""], ["regex", ""], ["Rails", "one line "]
, ["foo", ""], ["bar", "baz"] ]
L’appel transpose
le retourne, de sorte que vous avez une paire composée d’une liste de balises et d’une liste de textes de recherche:
[["Ruby", "regex", "Rails", "foo", "bar"], ["", "", "one line ", "", "baz"]]
Vous pouvez ensuite faire ce que vous voulez avec les résultats. Je pourrais suggérer, par exemple
search_str = texts.join(' ').strip.gsub(/\s+/, ' ')
Cela concaténera les extraits de recherche avec des espaces uniques, supprimera les espaces de début et de fin et remplacera les exécutions de plusieurs espaces par un seul espace.
'[Ruby] regex'.scan(/\[(.*?)\](.*)/)
reviendra
[["Ruby", " regex"]]
vous pouvez en savoir plus sur String # scan ici: http://Ruby-doc.org/core/classes/String.html#M000812 (en bref, il retourne un tableau de toutes les correspondances consécutives, le tableau extérieur de cette case est le tableau de correspondances et le groupe interne correspond aux groupes de capture de la correspondance).
pour faire l’affectation, vous pouvez la réécrire comme ceci (en supposant que vous n’ayez jamais qu’une seule correspondance dans la chaîne):
tag, keyword = '[Ruby] regex'.scan(/\[(.*?)\](.*)/).flatten
en fonction de exactement ce que vous voulez accomplir, vous voudrez peut-être changer la regex en
/^\s*\[(.*?)\]\s*(.+)\s*$/
qui correspond à toute la chaîne d'entrée et limite certains espaces du deuxième groupe de capture. Ancrer le motif au début et à la fin le rendra un peu plus efficace, et évitera dans certains cas d'obtenir des correspondances fausses ou dupliquées (mais cela dépend beaucoup de l'entrée) - cela garantit également que vous pouvez utiliser en toute sécurité le résultat renvoyé. tableau en affectation, car il n'y aura jamais plus d'une correspondance.
En ce qui concerne la question suivante, voici ce que je ferais:
def tags_and_keyword(input)
input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) do |match|
tags = match[0].split(/\]\s*\[/)
line = match[1]
return tags, line
end
end
tags, keyword = tags_and_keyword('[Ruby] [regex] [Rails] one line')
tags # => ["Ruby", "regex", "Rails"]
keyword # => "one line"
il peut être réécrit en une ligne, mais je ne le ferais pas:
tags, keyword = catch(:match) { input.scan(/^\s*\[(.+)\]\s+(.+)\s*$/) { |match| throw :match, [match[0].split(/\]\s*\[/), match[1]] } }
Ma solution suppose que toutes les balises viennent avant le mot-clé et qu'il n'y a qu'une balise/expression de mot-clé dans chaque entrée. La première capture englobe toutes les balises, mais ensuite, j'ai scindé cette chaîne. Il s’agit donc d’un processus en deux étapes (qui, comme @Tim l’a écrit dans son commentaire, est obligatoire sauf si vous disposez d’un moteur capable de faire une correspondance récursive).
Mettez ceci dans votre ApplicationHelper ou ailleurs
def element_id_for(f, element)
matcher = /id=(".*"|'.*')/
el_string = f.hidden_field(element.to_sym)
id_string = matcher.match(el_string)[0].gsub(/id="/, '').chomp('"')
return id_string
end
Enfin, vous pouvez utiliser cette méthode comme ceci:
form_for :test_form do |f|
my_id = element_id_for(f, :start_date)
# => "text_form_start_date"
end