1

I'm working with the LinkedIn API to get companies' details. They are sending an XML response, so I simply converted the XML to a hash using the .to_hash method This is a sample hash I'm getting: http://pastebin.com/1bXtHZ2F

in some companies they have more than one locations and contact information, i want to parse this data and get the details like phone number, city, postal_code etc.

The structure of the response is not consistent. Sometimes location field itself is missing or the postal_code is available only at the fourth location.

I tried two ways:

1.

def phone(locations)
  (locations && locations["values"][0]["contactInfo"]["phone1"]) || nil
end

This is not working if the phone number is not available in the first array

2.

def phone(locations)
  if locations["locations"]["total"].to_i == 1 
    locations["locations"]["location"]["contact_info"]["phone1"]
  else
    locations["locations"]["location"].each do |p|
      if (!p["contact_info"]["phone1"].nil? || !p['contact_info'].nil?)
        return p["contact_info"]["phone1"]
        break
      end
    end
  end
end

This is not working if the "location" hash itself is missing from the response. I need a solution where I can search with the keys "city", "phone" and "postal_code" and update if it is present. If it returns an array, parse the array and get the non-empty data.

I've also read this StackOverflow answer.

Community
  • 1
  • 1
Rajanand02
  • 1,303
  • 13
  • 19
  • So do you want to get the data only from locations which have all the elements (e.g. the first location that has city and postal_code and phone1) or are you happy to mix and match (city and postal_code from first location, phone1 from second location) – SteveTurczyn May 19 '14 at 17:57
  • no i need city and postal_code from the same location and phone number could be from any other location.This is more meaning full.You can't get city from one location and postal_code from other right? – Rajanand02 May 19 '14 at 18:44
  • So you're saying a locatio may be present but it may not have an address node, or it may not have a contact node. – SteveTurczyn May 19 '14 at 19:33
  • Yes and sometimes locations may be in an array..inside that array phone number can be at any position. – Rajanand02 May 20 '14 at 04:07

2 Answers2

1

I see this as a question about code confidence. That is, I'm betting you can figure out how to guess your way through all the possible conditions... but that will create a mess of unconfident code. Confident code states what it wants and it gets it and moves on. (Note: I get all of my inspiration on this topic from this wonderful book: http://www.confidentruby.com/ by Avdi Grimm).

That said, I'd recommend the following.

  1. Install the naught gem: https://github.com/avdi/naught
  2. In your code, utilize the Maybe conversion function (read through the gem documetnation for info) to confidently arrive at your values:

At the top of your class or controller:

NullObject = Naught.build

include NullObject::Conversions

In your method:

def phone(locations)
  return {} if locations["location"].blank?
  Maybe(locations["locations"])["location"].to_a.inject({}) do |location, acc|
    contact_info = Maybe(location["contact_info"])
    acc[location][:city] = contact_info["city1"].to_s
    acc[location][:phone] = contact_info["phone1"].to_i
    acc[location][:postal_code] = contact_info["postal_code1"].to_s
    acc
  end
end

I'm not sure exactly what you're trying to accomplish but the above may be a start. It is simply attempting to assume all of the keys exist. Whether they do or they don't they get converted to a object (an array, a string or an integer). And then, ultimately, collected into a hash (call acc -- short for "accumulator" -- internal to the loop above) to be returned.

If any of the above needs clarification let me know and we can chat.

pdobb
  • 17,688
  • 5
  • 59
  • 74
1

Ok, this code basically works through the hash and isn't concerned about node names (other than the specific nodes it's searching for)

the find_and_get_values method takes two arguments: object to search, and an array of nodes to find. It will only return a result if all nodes in the array are siblings under the same parent node. (so "city" and "postal_code" must be under the same parent otherwise neither is returned)

The data returned is a simple hash.

The get_values method takes one argument (the company hash) and calls find_and_get_values twice, once for %w(city postal_code) and once for %w(phone1) and merges the hash results into one hash.

def get_values(company)
  answer = {}
  answer.merge!(find_and_get_values(company["locations"], %w(city postal_code))
  answer.merge!(find_and_get_values(company["locations"], ["phone1"]))
  answer
end

def find_and_get_values(source, match_keys)
  return {} if source.nil?
  if source.kind_of?(Array)
    source.each do |sub_source|
      result = find_and_get_values(sub_source, match_keys)
      return result unless result.empty?
    end
  else
    result = {}
    if source.kind_of?(Hash)
      match_keys.each do |key|
        result[key] = source[key] unless source[key].nil?
      end
      return result if result.count == match_keys.count
      source.each do |sub_source|
        result = find_and_get_values(sub_source, match_keys)
        return result unless result.empty?
      end
    end
  end
  return {}
end

  p get_values(company)
SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53