Parse a call transcript into array of hash - Ruby

Viewed 78

I am parsing a call transcript. The content of the transcript comes back as a string formatted like the following:

"Operator: Hi, please welcome Bob Smith to the call. Bob Smith: Hello there, thank you for inviting me...Now I will turn the call over to Stacy. Stacy White: Thanks Bob. As he was saying...."

There is no new line when each new speaker starts speaking.

I would like to turn the above string into the an array of hash. Something like the following:

[ { speaker: "Operator",
    content: "Hi, please welcome Bob Smith to the call" },
  { speaker: "Bob Smith",
    content: "Hello there, thank you for inviting me...Now I will turn the call over to Stacy." }, 
  { speaker: "Stacy White",
    content: "Thanks Bob. As he was saying...." }
]

I think I would need to use some sort of regular expression to parse this, but have no idea how even after spending the morning reading up on it. Any help here would be much appreciated.

Thanks

Update:

To others that may find this useful, here's what I ended up coming up with using the suggested solution below:

def display_transcript
  transcript_pretty = []
  transcript = self.content
  transcript_split = transcript.split(/\W*([A-Z]\w*\W*\w+):\W*/)[1..-1]
  transcript_split_2d = transcript_split.each_slice(2).to_a
  transcript_split_2d.each do |row|
    blurb = { speaker: row[0], content: row[1]}
    transcript_pretty << blurb
  end

  return transcript_pretty
end
2 Answers

I can give you an expression you can use to break up the string. From there you can take it on yourself I'm sure, you wouldn't want me to take away the pleasure of reaching your goal is it ? :>)

string = "Operator: Hi, please welcome Bob Smith to the call. Bob Smith: Hello there, thank you for inviting me...Now I will turn the call over to Stacy. Stacy White: Thanks Bob. As he was saying...."
split_up = string.split(/\W*(\w*\W*\w+):\W*/)[1..-1]
Hash[*split_up]
# {"Operator"=>"Hi, please welcome Bob Smith to the call", "Bob Smith"=>"Hello there, thank you for inviting me...Now I will turn the call over to Stacy", "Stacy White"=>"Thanks Bob. As he was saying...."}

Some explanation: the regular expression looks for one or two words (\w*\W*\w+), eventually prepended with a dot and a space \W* and followed by a double point and eventually spaces following :\W* This expression is used to split the string in an array. The result always has an empty string to start with so you get rid of that by the [1..-1] Next you convert that Array into a Hash, the first element is the key, the second the value and so on until the end of the Array.

R = /(\S[^:]*):\s*([^:]*[.?!])/
def str_to_hash(str)
  str.gsub(r).with_object({}) { |_,h| h[$1]=$2 }
end
str = "Operator: Hi, please welcome Bob Smith to the call. Bob Smith: Hello there, thank you for inviting me...Now I will turn the call over to Stacy. Stacy White: Thanks Bob. As he was saying...."
str_to_hash(str)
  #=> {"Operator"=>"Hi, please welcome Bob Smith to the call.",
  #=>  "Bob Smith"=>"Hello there, thank you for inviting me...Now I will turn the call over to Stacy.",
       "Stacy White"=>"Thanks Bob. As he was saying...."}
str = "Operator: Bob Smith, what's the value?   Bob Smith: $100,000 or so. Stacy? Stacy White: Thanks Bob. I agree...."
str_to_hash(str)
  #=> {"Operator"=>"Bob Smith, what's the value?",
  #    "Bob Smith"=>"$100,000 or so. Stacy?",
  #    "Stacy White"=>"Thanks Bob. I agree...."}

You can see the regex in action here.

This uses the form of String#gsub that takes one argument (here a regular expression) and no block, returning an enumerator that is chained to Enumerator#with_object.1

We can write the regular expression in free-spacing mode to make it self-documenting.

R = /
    (           # begin capture group 1
      \S        # match a character other than a whitespace
      [^:]*  # match 0+ characters other than a colon
    )           # end capture group 1
    :           # match a colon
    \s*         # match 0+ whitespaces
    (           # begin capture group 2
      [^:]*     # match 0+ characters other than a colon
      [.?!]     # match a period, question mark or exclamation mark
    )           # end capture group 2
    /x          # free-spacing regex definition mode 

Because [^:]* is greedy [.?!] will match the last period, question mark or exclamation mark that precedes a colon or the end of the string.

1 Note that this form of String#gsub has nothing to do with character replacement. It simply returns matches, which are held by the first block variable, _. An underscore is used for that block variable to signify that it is not used in the block.

Related