1
votes

Comment pousser une chaîne dans un nouveau tableau dans Ruby

Je souhaite rechercher des sous-chaînes dans une chaîne donnée. Chaque fois que la sous-chaîne est incluse dans la chaîne saisie, je l'ajoute à un tableau. En fin de compte, je veux compter ce tableau pour obtenir le nombre de fois où chaque sous-chaîne apparaît.

Le problème est que la sous-chaîne du dictionnaire dans mon code n'est ajoutée qu'une seule fois à new_array .

Par exemple:

def substrings(word, array) 

  new_array = []

  array.each do |index| 

    if word.downcase.include? (index)

      new_array << index

    end
  end

  puts new_array.tally

end

 dictionary = ["below", "down","go","going","horn","how","howdy","it","i","low","own","part","partner","sit"]

 substrings("go going", dictionary)

Devrait afficher:

{"go"=>1, "going"=>1, "i"=>1}

mais j'obtiens

{"go"=>2, "going"=>1, "i"=>1}

Voici mon code:

dictionary = ["below", "down","go","going","horn","how","howdy","it","i","low","own","part","partner","sit"]

substrings("go going", dictionary)


1 commentaires

Astuce: Pour les longues listes de mots individuels, utilisez % w [a b c ...] qui vous donne le même résultat sans tous les guillemets et virgules supplémentaires.


7 Réponses :


0
votes

Vous pouvez utiliser scan pour compter combien de fois chaque sous-chaîne apparaît.

def substrings(word, array)
  output = {}
  array.each do |index|
     count_substring_appears = word.scan(index).size
     if count_substring_appears > 0
       output[index] = count_substring_appears
     end
  end

  output
end


0 commentaires

0
votes

Seuls les mots "go", "going" et "i" de votre dictionnaire sont des sous-chaînes de votre phrase. Chacun de ces mots n'apparaît qu'une seule fois dans le dictionnaire. Donc new_array contient ["go", "going", "i"] qui exactement {"go" => 1, "going" => 1, "i" => 1} .

Je suppose que vous vous attendiez à ce que go soit deux fois parce que c'est deux fois dans votre phrase. Dans ce cas, vous pouvez changer votre méthode en

def substrings(word, array) 
  new_array = []
  array.each do |index| 
    word.scan(/#{index}/).each { new_array << index }
  end
  puts new_array.tally
end

word.scan (/ # {index} /) renvoie chaque occurrence de sous-chaîne dans votre phrase .


3 commentaires

La solution de @byakugie est encore plus propre


C'est plus propre, mais je me retrouve à aimer le vôtre. J'apprends juste le rubis et le vôtre est le plus facile à digérer pour moi en ce moment. Merci!


@Maniaci Je suis content si vous le trouvez utile. Je voulais remplacer le moins possible, juste pour mettre en évidence le problème dans la méthode d'origine.



0
votes

Vous devez compter le nombre de fois qu'une chaîne apparaît dans l'index, utilisez donc scan:

def substrings(word, array) 

  hash = {}

  array.each do |index| 
    if word.downcase.include? (index)
      new_hash = {index => word.scan(/#{index}/).length}; 
      hash.merge!(new_hash) 
    end
  end

  puts hash 

end


0 commentaires

1
votes

Selon la taille de votre dictionnaire.

Vous pouvez simplement mapper tous les éléments avec leur nombre d'occurrences lorsque la sous-chaîne existe dans le mot.

dictionary.map {|w| [w,word.scan(w).size] if word.include?(w)}.compact.to_h


2 commentaires

Je l'aime mieux que le mien. "Élégant!"


@theTinMan merci et honnêtement si vous regardez mes modifications, mon original était similaire mais pire que le vôtre.



0
votes

Si je comprends bien qu'on nous donne un tableau dictionnaire de mots ne contenant aucun espace, et une chaîne str , et que nous devons construire un hachage dont les clés sont des éléments de dictionary et dont les valeurs sont égales au nombre de sous-chaînes 1 non superposées de str pour lesquelles la clé est une sous-chaîne. Le hachage retourné doit exclure les clés ayant des valeurs de zéro.

Cette réponse résout le cas où, dans:

substr_counts(str)
  #=> {"l"=>3, "o"=>2, "w"=>2, "n"=>3, "e"=>3, "r"=>3, "lo"=>2,
  #    ...
  #    "wnliest"=>1, "lownlies"=>1, "ownliest"=>1, "lownliest"=>1} 
substr_counts(str).size
  #=> 109

dictionary est grand, str n'est pas trop volumineux (la signification que j'élaborerai plus tard) et l'efficacité est importante.

Nous commençons par définir une méthode d'aide, dont le but deviendra clair. p >

str = "lowner partnership lownliest"
cover_count(str, dictionary)
  #=> {"i"=>2, "low"=>2, "own"=>2, "part"=>1, "partner"=>1}     

Pour l'exemple donné dans la question,

cover_count("go going", dictionary)
  #=> {"go"=>2, "going"=>1, "i"=>1}

Comme on l'a vu, cette méthode décompose str en words, calcule chaque sous-chaîne de chaque mot et renvoie un hachage dont les clés sont les sous-chaînes et dont les valeurs sont le nombre total de sous-chaînes non superposées dans tous les mots qui contiennent cette sous-chaîne.

Le hachage souhaité peut maintenant être construit rapidement.

dictionary = ["below", "down", "go", "going", "horn", "how", "howdy", 
              "it", "i", "low", "own", "part", "partner", "sit"]

def cover_count(str, dictionary)
  h = substr_counts(str)
  dictionary.each_with_object({}) do |word,g|
    g[word] = h[word] if h.key?(word)
  end
end

p >

substr_counts("go going")
  #=> {"g"=>3, "o"=>2, "go"=>2, "i"=>1, "n"=>1, "oi"=>1, "in"=>1, "ng"=>1,
  #    "goi"=>1, "oin"=>1, "ing"=>1, "goin"=>1, "oing"=>1, "going"=>1}

Un autre exemple:

def substr_counts(str)
  str.split.each_with_object(Hash.new(0)) do |word,h|
    (1..word.size).each do |sub_len|
      (0..word.size-sub_len).each do |start_idx|
        h[word[start_idx,sub_len]] += 1
      end
    end
  end
end       

Ici,

substrings(str, dictionary)

Il y a un compromis évident ici. Si str est long, et surtout s'il contient des mots longs 2 , il faudra trop de temps pour construire h pour justifier les économies de ne pas avoir pour déterminer, pour chaque mot du dictionnaire , si ce mot est contenu dans chaque mot de str . Si, cependant, cela vaut la peine de construire h , le gain de temps global pourrait être substantiel.

1. Par "sans chevauchement", j'entends que si str est égal à 'bobobo' , il contient une, et non deux, sous-chaînes 'bobo' . sup>

2. substr_counts ("antidisestablishmentarianism"). size # => 385 , pas si mal.


0 commentaires

0
votes

Une autre option consiste à utiliser Array # product après la séparation du mot, vous pouvez donc utiliser Enumerable # Tally comme vous le souhaitez:

word = "go going"
word.split.product(dictionary).select { |a, b| a.include? b }.map(&:last).tally

#=> {"go"=>2, "going"=>1, "i"=>1}

Il ne produit pas la même chose lorsque word = "gogoing" , car il est divisé en un tableau d'éléments. Donc, je ne peux pas dire si c'est le comportement que vous recherchez.


0 commentaires

0
votes

Je commencerais par ceci:

dictionary = %w[down go going it i]
target = 'go going'

dictionary.flat_map { |w|
  target.scan(Regexp.new(w, Regexp::IGNORECASE))
}.reject(&:empty?).tally
# => {"go"=>2, "going"=>1, "i"=>1}


0 commentaires