2

How do you extract information from JSON format without using any gems? The string is as follows

[["B",90461],["B",85547],["B",85709],["B",85029],
["B",85487],["B",83419],["B",83411],["B",85311],["B",81335],
["B",85149],["B",85959]]

I need the integer part only and the string can be converted to any types of data. The only requirement is do not use any gems. Thanks a lot.

3
  • this is not a valid JSON object, can you please post the right thing Commented Feb 16, 2014 at 15:09
  • 1
    What have you tried, and what was the problem with it? Also, could you please be clearer about requirement for "no gems", because require "json" and the JSON module are available in Ruby standard lib (no gems need be installed)? Commented Feb 16, 2014 at 18:38
  • @bjhaid - It's valid JSON Commented Feb 16, 2014 at 21:19

4 Answers 4

2

Use Ruby's stdib JSON for the same :

require 'json'

a = '[["B",90461],["B",85547],["B",85709],["B",85029],
["B",85487],["B",83419],["B",83411],["B",85311],["B",81335],
["B",85149],["B",85959]]'

JSON.parse(a).map(&:last)
# => [90461,
#     85547,
#     85709,
#     85029,
#     85487,
#     83419,
#     83411,
#     85311,
#     81335,
#     85149,
#     85959]

Also look at this method : JSON::parse .

Sign up to request clarification or add additional context in comments.

1 Comment

I realized that the OP says that the requirement is not to use a gem, and "json" is a gem.
1
'[["B",90461],["B",85547],["B",85709],["B",85029], ["B",85487],["B",83419],["B",83411],["B",85311],["B",81335], ["B",85149],["B",85959]]'
.scan(/\d+/).map(&:to_i)
# => => [90461, 85547, 85709, 85029, 85487, 83419, 83411, 85311, 81335, 85149, 85959]

Comments

1

Working from these assumptions:

  • for some reason, you can't simply use Ruby's stdlib JSON library, as suggested by many others
  • the "B" strings in your incoming data may be replaced by any other single valid JSON object, however complex; e.g. { "key1": ["ABC", { "key2": "{\"key3\": [\"DEF\"]}" }]}
  • you want only and exactly the second values (always integers) of arrays nested one level deep in a top-level array; e.g. [[..., 123], [..., 456]] yields [123, 456]

Here is your function:

def parse_integers_from_specific_JSON (string)
  ary_depth = 0     # Level of nesting within arrays
  obj_depth = 0     # Level of nesting within objects
  in_string = false # Flag for whether the parser is inside a string
  escaped = false   # Flag for whether the next character will be escaped
  int_buffer = ""   # String buffer for integer digits
  integers = []     # Result array of integers

  string.each_char do |c|
    if (in_string) && !(escaped) && (c == '\\')
      # When an escape character is found in a string, set the 'escaped' flag
      escaped = true
      next
    end

    if (c == '"')
      if !in_string
        # When a quote is found outside a string, enter the new string
        in_string = true

      elsif escaped
        # When an escaped quote is found within a string, ignore it

      else
        # When an unescaped quote is found within a string, exit the string
        in_string = false
      end
    end

    # If relevant, the 'escaped' flag has escaped its quote by now; unset it
    escaped = false

    if !(in_string) && (c =~ /\d/) && (obj_depth == 0) && (ary_depth == 2)
      # If a digit is encountered at the correctly nested position,
      # append it to the integer buffer string
      int_buffer << c

    elsif !(int_buffer.empty?)
      # If the integer buffer string has digits in it and the current character
      # is not a digit, flush the contents of the buffer into the result array
      integers << int_buffer.to_i
      int_buffer.clear
    end

    unless in_string
      # Check for unquoted special characters and adjust depth
      case c
      when '{' then obj_depth += 1
      when '}' then obj_depth -= 1
      when '[' then ary_depth += 1
      when ']' then ary_depth -= 1
      end
    end
  end

  return integers
end

Here's a snippet showing it in action, using a beefed-up version of your sample data:

incoming_data_string = (<<-JSON).strip
[
  ["B",90461],
  [[],85547],
  [["A"],85709],
  ["",85029],
  [[123,456],85487],
  [[456,[789],"A"],83419],
  [{ a: [123] },83411],
  ["B",85311],
  ["B",81335],
  ["B",85149],
  [{ "key1": ["ABC", { "key2": "{\\"key3\\": [\\"DEF\\"]}" }]},85959]
]
JSON

p parse_integers_from_specific_JSON(incoming_data_string)

#=> [90461, 85547, 85709, 85029, 85487, 83419, 83411, 85311, 81335, 85149, 85959]

This won't be particularly fast. If you want it to be fast, the correct answer is to require 'json' and use JSON::parse.

Comments

0

I guess there is a json module in standard library (link).

Also simple solution without parsing (but with eval, which is evil):

eval('[["B",111111],["B",222222],["B",333333]]').map(&:last)

3 Comments

don't teach people to use eval avoid it when you can as almost everytime you use it, there is a safer alternative
@bjhaid You sound like a parrot repeating another person's words without thinking what they mean. Why are data with pairs of alphabet string and a numeral not safe?
@sawa this is a json object most likely received from an external service that you cannot be certain of what it's content would always be, I am not repeating anyone's word but sure of what I am saying, and most times eval is used there is a safer alternative

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.