5

What is the best way to turn the following Ruby String into an Array (I'm using ruby 1.9.2/Rails 3.0.11)

Rails console:

>Item.first.ingredients
=> "[\"Bread, whole wheat, 100%, slice\", \"Egg Substitute\", \"new, Eggs, scrambled\"]"
>Item.first.ingredients.class.name
=> "String"
>Item.first.ingredients.length
77

The desired output:

>Item.first.ingredients_a
["Bread, whole wheat, 100%, slice", "Egg Substitute", "new, Eggs, scrambled"]
>Item.first.ingredients_a.class.name
=> "Array
>Item.first.ingredients_a.length
=> 3

If I do this, for instance:

>Array(Choice.first.ingredients)

I get this:

=> ["[\"Bread, whole wheat, 100%, slice\", \"Egg Substitute\", \"new, Eggs, scrambled\", \"Oats, rolled, old fashioned\", \"Syrup, pancake\", \"Water, tap\", \"Oil, olive blend\", \"Spice, cinnamon, ground\", \"Seeds, sunflower, kernels, dried\", \"Flavor, vanilla extract\", \"Honey, strained/extracted\", \"Raisins, seedless\", \"Cranberries, dried, swtnd\", \"Spice, ginger, ground\", \"Flour, whole wheat\"]"] 

I'm sure there must be some obvious way to solve this.

For clarity, this will be editable in a textarea field in a form, so should be made as secure as possible.

2
  • 1
    The best way to do it is to modify the ingredients method. Commented May 10, 2012 at 1:07
  • 1
    If you can't change the ingredients method, you should at least post it so we can see what it's outputting. Overriding it is certainly an option, but we'd have to see it to help. Commented May 10, 2012 at 1:54

6 Answers 6

9

What you have looks like JSON, so you can do:

JSON.parse "[\"Bread, whole wheat, 100%, slice\", \"Egg Substitute\", \"new, Eggs, scrambled\"]"
#=> ["Bread, whole wheat, 100%, slice", "Egg Substitute", "new, Eggs, scrambled"]

this avoids a lot of the horrors of using eval.

Though you should really think about why you're storing your data like that in the first place, and consider changing it so you don't have to do this. Further, it's likely that you should be parsing it to an array in ingredients so that the method returns something more meaningful. If you're almost always doing the same operation on a method's return value, the method is wrong.

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

2 Comments

I would only do this if it was, in fact, JSON. Only a look at the ingredients method would tell.
@MarkThomas Wholly agreed, but there isn't a lot of information given about why the data is in the string format that it is, so one (unfortunately) has to make assumptions.
5
class Item
  def ingredients_a
    ingredients.gsub(/(\[\"|\"\])/, '').split('", "')
  end
end
  1. strip off the extraneous characters
  2. split into array elements using the separating pattern

2 Comments

I really liked the JSON.parse answer, but this one should probably work and is probably the most direct solution to the issue.
+1 This is the most direct answer to the question. An elegant answer requires more information about how the array became a string in the first place.
1

It looks like the ingredients method returns the .inspect output of the resulting return array. It is not very useful output that way. Do you have the ability to change it to return a plain array?

What I would not do is use eval, which will just increase the hackiness of already hacky code.

5 Comments

I agree with you about wanting to avoid 'eval' if possible. I could potentially override the method or make other calls, if necessary, but I'd still have the question of what is the best way to convert the string to the array, because the data type might not change. I could potentially store the data in a serializable text field and store the array there in some format I guess.
@NathanW I think the problem is, why does is your array in string form in the first place?
+1 While this seems more like a comment, it's pretty close to what is probably the correct answer with the limited given information.
@andrew, I'm allowing editing of an Array via a textarea in a form. This example is how the information arrives via params. If there are other, better methods to maintain an array, that would be great to hear about.
@NathanW Then I presume it is JSON being sent? If it isn't, it should be.
1

Like Mark Thomas said, modify your ingredients method, unless you really want two separate methods that return a string and an array, respectively. I'll assume you really only want to return an array. For the sake of argument, let's say your ingredients method currently returns a variable named ingredients_string. Modify your method like so:

def ingredients
  ...
  ingredients_array = ingredients_string.split('"')
  ingredients_array.delete_if { |element| %(", "[", "]").include? element }
  ingredients_array
end

1 Comment

For the record, I think Andrew Marshall's JSON.parse suggestion is much more elegant.
0

Not sure if this comes into play, but what happens if you want to use an array as an attribute, you might want to consider serialize..

class Item << ActiveWhatever
  serialize :ingredients, Array

  ...
end

More info on serialize here http://api.rubyonrails.org/classes/ActiveRecord/Base.html

1 Comment

Yes, so I suppose I could potentially store data into a serializable Array field, but I'm still starting off with a string that looks like the format above :).. so would need to convert it to an array to get it in there :).
0

If your string-array is standard JSON format, just use JSON.parse()

Comments

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.