awojo
awojo

Reputation: 967

Sequentially parse array to hash in Ruby

I have an array that looks like this:

array = [
  "timestamp 1",
  "data 1",
  "data 2",
  "data 3",
  "timestamp 2",
  "data "1",
  "timestamp 3",
  ".." 
]
  etc

I want to loop through my array, and turn it into a hash data structure that looks like:

hash = { 
  "timestamp 1" => [ "data 1", " data 2", "data 3" ],
  "timestamp 2" => [ "data 1" ],
}

I can't figure out a good "rubyish" way of doing it. I'm looping through the array, and I just quite can't seem to figure out how to keep track of where I am at, and assign to the hash as needed.

# Let's comb through the array, and map the time value to the subsequent lines beneath
array.each do |e|
  if timestamp?(e)
    hash["#{e}"] == nil
  else
    # last time stamp here => e
end

EDIT: Here is the timestamp? method

def timestamp?(string)
  begin
    return true if string =~ /[a-zA-z][a-z][a-z]\s[a-zA-z][a-z][a-z]\s\d\d\s\d\d:\d\d:\d\d\s\d\d\d\d/
    false
  rescue => msg
    puts "Error in timestamp? => #{msg}"
    exit
  end
end

Upvotes: 0

Views: 190

Answers (7)

hirolau
hirolau

Reputation: 13911

I know this has already been answered, but there are so many ways to do this.

I prefer these two ways, they might not be fast but i find them readable:

my_hash = Hash.new
array.slice_before(/timestamp/).each do |array|
  key, *values = array
  my_hash[key] = values
end

or

one_liner = Hash[array.slice_before(/timestamp/).map{|x|[x.shift, x]}]

Upvotes: 0

Aaron Cronin
Aaron Cronin

Reputation: 2103

hash = (Hash.new { |this, key| this[key] = [] } ).tap do |hash|
  current_timestamp = nil
  array.each do |element|
    current_timestamp = element if timestamp? element
    hash[current_timestamp] << element unless timestamp? element
  end
end

Using an outside variable to keep track of the current timestamp, but wrapping it in a closure to avoid polluting the namespace.

Upvotes: 0

Daiku
Daiku

Reputation: 1227

array = [
  "timestamp 1",
  "data 1",
  "data 2",
  "data 3",
  "timestamp 2",
  "data 1",
  "timestamp 3",
  "data 2" 
]

hsh = {}
ary = []

array.each do |line|
  if line.start_with?("timestamp")
    ary = Array.new
    hsh[line] = ary
  else 
    ary << line
  end
end

puts hsh.inspect

Upvotes: 2

sawa
sawa

Reputation: 168121

Hash[array.slice_before{|e| e.start_with?("timestamp ")}.map{|k, *v| [k, v]}]

Output

{
  "timestamp 1" => [
    "data 1",
    "data 2",
    "data 3"
  ],
  "timestamp 2" => ["data 1"],
  "timestamp 3" => [".."]
}

Upvotes: 1

Rob Di Marco
Rob Di Marco

Reputation: 44952

last_timestamp = nil
array.reduce(Hash.new(){|hsh,k| hsh[k]=[]}) do |hsh, m|
  if m =~ /timestamp/
    last_timestamp = m
  else
    hsh[last_timestamp] << m
  end
  hsh
end

Upvotes: 0

Simon Perepelitsa
Simon Perepelitsa

Reputation: 20639

You can keep track of the last hash key using an outside variable. It will be persisted across all iterations:

h = {}
last_group = nil
array.each do |e|
  if timestamp?(e)
    array[e] = []
    last_group = e
  else
    h[last_group] << e
  end
end

Upvotes: 0

Arup Rakshit
Arup Rakshit

Reputation: 118271

I would do as below:

array = [
  "timestamp 1",
  "data 1",
  "data 2",
  "data 3",
  "timestamp 2",
  "data 1", 
]

Hash[array.slice_before{|i| i.include? 'timestamp'}.map{|a| [a.first,a[1..-1]]}]
# => {"timestamp 1"=>["data 1", "data 2", "data 3"], "timestamp 2"=>["data 1"]}

Upvotes: 2

Related Questions