2010-04-08 6 views
0

je un tableau de balises par élément comme ceci:Ruby: balises carte dans une condition booléenne pour obtenir un vrai/faux résultat

item1 = ['new', 'expensive'] 
item2 = ['expensive', 'lame'] 

J'ai aussi une expression booléenne comme une chaîne basée sur les balises possibles: Comment puis-je déterminer si un article correspond aux critères d'achat en fonction des étiquettes qui lui sont associées? Comment puis-je déterminer si un article correspond aux critères d'achat en fonction des étiquettes qui lui sont associées? Ma pensée originale était de faire un gsub sur tous les mots dans buy_it pour devenir 'true' ou 'false' basé sur eux existant dans le tableau itemx tags, puis exec la chaîne résultante pour obtenir un résultat booléen.

Mais comme la communauté Ruby est généralement plus créative que moi, existe-t-il une meilleure solution?

EDIT:

Juste pour clarifier, buy_it dans mon exemple est dynamique, les utilisateurs peuvent modifier les critères d'acheter quelque chose à l'exécution.

+0

Votre solution est en effet d'une grande beauté (et économise beaucoup d'informatique banale ..)! Fonce! Etes-vous sûr que vous devez le faire dans ruby ​​au lieu de certaines conditions génératrices de SQL? – clyfe

+0

Ce n'est pas une application web/Rails, donc il n'y a pas de base de données ici. – cgyDeveloper

Répondre

1

Le long des lignes de votre idée gsub, au lieu de remplacer chaque mot pour vrai/faux à chaque fois, pourquoi ne pas remplacer chaque « requête » dans une expression qui peut être réutilisée, par exemple l'exemple buy_it:

buy_it = "(new || expensive) && !lame" 
buy_it_expr = buy_it.gsub(/(\w+)/, 'tags.include?("\1")') 

puts buy_it_expr 
=> (tags.include?("new") || tags.include?("expensive")) && !tags.include?("lame") 

Il pourrait être évalué dans un Proc et utilisé comme ceci:

buy_it_proc = eval "Proc.new { |tags| #{buy_it_expr} }" 

buy_it_proc.call(item1) 
=> true 
buy_it_proc.call(item2) 
=> false 

Bien sûr, il faut veiller à ce que l'expression ne contient pas de code malveillant. (Une solution pourrait être de supprimer tous les caractères d'opérateur autorisés à l'exception de eval.)

+0

Pour d'autres accélérations, on pourrait envisager de basculer le mécanisme de recherche de 'Array.include?' Et/ou le format de stockage de balises de 'String' à quelque chose de plus rapide à comparer, par ex. ': symboles'. L'automatisation de cette conversion pourrait même utiliser une astuce similaire. – Arkku

+0

Ouais, c'est dans le sens de ce que je pensais. Je ne savais pas que je pourrais faire descendre le gsub à une ligne (je pensais que je devrais faire un scan en premier), ce qui le rend plutôt lisse. La seule chose que cette solution ne peut pas gérer sont les balises qui sont des sous-chaînes d'autres balises telles que buy_it = "cool || cooler". (Je suppose que je pourrais juste faire une règle que les balises valides ne peuvent pas être des sous-chaînes d'autres balises.) – cgyDeveloper

+0

Il gère très bien les sous-chaînes: '" cool || cooler ".gsub (/ (\ w +) /, 'tags.include? ("\ 1") ') 'produit' tags.include? ("Cool") || tags.include? ("cooler") '. – Arkku

0

Un hachage est un bon candidat ici.

items = {'ipad' => ['new', 'expensive'], 'kindle' => ['expensive', 'lame']} 
items.each do |name,tags| 
    if tags.include?('new' || 'expensive') && !tags.include?('lame') 
    puts "buy #{name}." 
    else 
    puts "#{name} isn't worth it." 
    end 
end 
+0

Cela fonctionnerait pour cette situation particulière, mais buy_it est en fait une chaîne spécifiée par l'utilisateur, donc la logique doit être dynamique. – cgyDeveloper

Questions connexes