• extract into a method - struggling to abstract

    From Sayth Renshaw@21:1/5 to All on Sun Jul 3 07:01:31 2016
    Hi

    I have working code that I have currently loops through an example returning matches via nokogiri module.

    This works

    require 'nokogiri'
    require 'awesome_print'

    @doc = Nokogiri::XML(File.open('/home/sayth/data/20160702RHIL0.xml'))

    @doc.search('race').map do |race|
    nominations = race.search('nomination')
    .map do |nomination|
    {
    number: nomination['number'].to_i,
    id: nomination['id'].to_i,
    horse: nomination['horse'].to_s,
    age: nomination['age'].to_i,
    sex: nomination['sex'].to_s,
    colour: /\W(.+?)\d?\s/.match(nomination['description'].to_s)[1]
    }
    end

    a = { race['id'].to_i => nominations }
    ap a
    end

    _______________________________________________________________________________

    So at this point I know that I need to replicate this for 6 sections in total, logically to me then I want to abstract especially the middle.

    {
    number: nomination['number'].to_i,
    id: nomination['id'].to_i,
    horse: nomination['horse'].to_s,
    age: nomination['age'].to_i,
    sex: nomination['sex'].to_s,
    colour: /\W(.+?)\d?\s/.match(nomination['description'].to_s)[1]
    }

    and substitute a variable into race.search('nomination') this to replace the word nomination and to then replace all 'nomination' in the above section.

    So this is what I have done.

    data = %w(number id horse age sex colour)

    def extract_value(path_id, data)
    @doc.search('race').map do |race|
    puts path_id
    nominations = race.search(path_id)
    .map do | |
    data
    end
    values = { race['id'].to_i => nominations }
    ap values
    return values
    end
    end

    extract_value('nomination', data)

    _______________________________________________________________________________

    The main part I cannot wrap my head around is how to get these sections in without replicating it in the definition.

    number: nomination['number'].to_i,

    How can I better abstract this out?

    Cheers

    Sayth

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Klemme@21:1/5 to Sayth Renshaw on Sun Jul 3 18:52:29 2016
    On 03.07.2016 16:01, Sayth Renshaw wrote:
    Hi

    I have working code that I have currently loops through an example returning matches via nokogiri module.

    This works

    require 'nokogiri'
    require 'awesome_print'

    @doc = Nokogiri::XML(File.open('/home/sayth/data/20160702RHIL0.xml'))

    You are not closing the file here. Better:

    @doc = File.open('/home/sayth/data/20160702RHIL0.xml', 'rb') do |io|
    Nokogiri::XML(io)
    end

    @doc.search('race').map do |race|
    nominations = race.search('nomination')
    .map do |nomination|
    {
    number: nomination['number'].to_i,
    id: nomination['id'].to_i,
    horse: nomination['horse'].to_s,
    age: nomination['age'].to_i,
    sex: nomination['sex'].to_s,
    colour: /\W(.+?)\d?\s/.match(nomination['description'].to_s)[1]
    }
    end

    a = { race['id'].to_i => nominations }
    ap a
    end

    _______________________________________________________________________________

    So at this point I know that I need to replicate this for 6 sections in total, logically to me then I want to abstract especially the middle.

    {
    number: nomination['number'].to_i,
    id: nomination['id'].to_i,
    horse: nomination['horse'].to_s,
    age: nomination['age'].to_i,
    sex: nomination['sex'].to_s,
    colour: /\W(.+?)\d?\s/.match(nomination['description'].to_s)[1]
    }

    and substitute a variable into race.search('nomination') this to replace the word nomination and to then replace all 'nomination' in the above section.

    I would replace the variable name "nomination" by something more
    abstract, e.g. "node".irb

    So this is what I have done.

    data = %w(number id horse age sex colour)

    def extract_value(path_id, data)
    @doc.search('race').map do |race|
    puts path_id
    nominations = race.search(path_id)
    .map do | |
    data
    end
    values = { race['id'].to_i => nominations }
    ap values
    return values
    end
    end

    extract_value('nomination', data)

    I think you are almost there:

    data = %w(number id horse age sex colour)

    def extract_value(path_id, data)
    @doc.search('race').map do |race|
    puts path_id

    extracted_data =
    race.search(path_id).map do |node|
    {}.tap do |hash|
    data.each {|d| hash[d.to_sym] = Integer(node[d])}
    end
    end

    values = { race['id'].to_i => extracted_data }
    ap values

    # next line will prematurely return from the method,
    # do you want that?
    return values
    end
    end

    extract_value('nomination', data)

    _______________________________________________________________________________

    The main part I cannot wrap my head around is how to get these sections in without replicating it in the definition.

    number: nomination['number'].to_i,

    How can I better abstract this out?

    See above. Is this what you intended?

    Kind regards

    robert


    --
    remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Sayth Renshaw@21:1/5 to All on Mon Jul 4 04:27:33 2016
    This is close.


    data = %w(number id horse age sex colour)

    def extract_value(path_id, data)
    @doc.search('race').map do |race|
    puts path_id

    extracted_data =
    race.search(path_id).map do |node|
    {}.tap do |hash|
    data.each {|d| hash[d.to_sym] = Integer(node[d])}
    end
    end

    values = { race['id'].to_i => extracted_data }
    ap values

    # next line will prematurely return from the method,
    # do you want that?
    return values
    end
    end

    extract_value('nomination', data _______________________________________________________________________________

    However not all values are integers, some are strings.

    Can you make templates that you can drop into the function?

    I started down the path of trying to build a factory to generate structures based on input type and while it works I think creating a template like I would in web would make sense.

    Creating template ____________________________________________________________________
    def data_form(_id, path = 'nomination')
    terms = %w(one two three)
    noms = terms.collect { |x| path.to_s + "[#{x}].to_i" }
    puts noms
    end
    ____________________________________________________________________

    If I just defined the templates as:

    structure = {
    number: nomination['number'].to_i,
    id: nomination['id'].to_i,
    horse: nomination['horse'].to_s,
    age: nomination['age'].to_i,
    sex: nomination['sex'].to_s,
    colour: /\W(.+?)\d?\s/.match(nomination['description'].to_s)[1]
    }

    Then the method could be really simplified to just take in the node and the template to use.

    Just Like

    def extract_value(_node, data)
    term = 'node' + 's'
    @doc.search('race').map do |race|
    term = race.search('node')
    .map do |_node|
    data
    end
    end
    a = { race['id'].to_i => term }
    ap a
    end

    extract_value('nomination', structure)

    Sayth

    PS I learnt tap from your previous post I had thought it was a typo of map but no http://ruby-doc.org/core-2.3.1/Object.html#method-i-tap :-)

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Sayth Renshaw@21:1/5 to All on Mon Jul 4 05:58:46 2016
    for example template using json

    noms.json

    {
    "number": "nomination['number'].to_i",
    "id": "nomination['id'].to_i",
    "horse": "nomination['horse'].to_s",
    "age": "nomination['age'].to_i",
    "sex": "nomination['sex'].to_s",
    "colour": "/\W(.+?)\d?\s/.match(nomination['description'].to_s)[1]"
    }

    json.rb

    require 'json'

    file = File.read('noms.json')
    data_hash = JSON.parse(file)
    ary = 0..data_hash.length
    ary.each do |x|
    out = "#{data_hash.keys[x]}: #{data_hash.values[x]}"
    puts out
    end

    _____________________________________________________________
    out

    number: nomination['number'].to_i
    id: nomination['id'].to_i
    horse: nomination['horse'].to_s
    age: nomination['age'].to_i
    sex: nomination['sex'].to_s
    colour: /W(.+?)d?s/.match(nomination['description'].to_s)[1]

    Is this a good idea to go down?

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)
  • From Robert Klemme@21:1/5 to Sayth Renshaw on Tue Jul 5 23:21:55 2016
    On 04.07.2016 13:27, Sayth Renshaw wrote:
    This is close.

    However not all values are integers, some are strings.

    Can you make templates that you can drop into the function?

    Just a rough idea


    data = {
    'number' => :to_i,
    'id' => :to_i,
    'horse' => nil,
    'age' => :to_i,
    'sex' => :to_s,
    'colour' => lambda {|x| "Color #{x}"},
    }

    def extract_value(path_id, data)
    @doc.search('race').map do |race|
    puts path_id

    extracted_data =
    race.search(path_id).map do |node|
    {}.tap do |hash|
    data.each {|d, m|
    val = node[d]
    hash[d.to_sym] = m ? m.to_proc[val] : val)
    }
    end
    end

    values = { race['id'].to_i => extracted_data }
    ap values

    # next line will prematurely return from the method,
    # do you want that?
    return values
    end
    end

    There is room for optimization, i.e. avoiding repeated to_proc calls.

    PS I learnt tap from your previous post I had thought it was a typo of map but no http://ruby-doc.org/core-2.3.1/Object.html#method-i-tap :-)

    :-)

    Kind regards

    robert


    --
    remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)