0

I have an array that stores banned IP addresses in my application:

bannedips = ["10.10.10.10", "20.20.20.20", "30.30.30.30"]

I want to add more information to each banned IP address (IP address, ban timestamp, ban reason).

How can I do this in Ruby?

5
  • 2
    Use hashes. bannedips = [{ip: "10.10.10.10", reason: "bla bla bla", etc...}, {...}] Or if you want to be super fancy and object-oriented, make a BannedIP class and store an array of those. Commented Jan 2, 2015 at 17:20
  • @Adrian hash idea looks good. What can/should I use to retrieve the time stamp and reason for a given banned IP address? Commented Jan 2, 2015 at 17:23
  • The syntax looks like bannedips[0][:ip]. Read the documentation. Commented Jan 2, 2015 at 17:25
  • Nobody has asked how many IPs you're trying to track, so I will. How many are you? A Hash, a Struct, an OpenStruct, a Set, or a custom-defined class could all satisfy your needs, but if you're trying to deal with thousands of IPs, then keeping them in memory is a bad idea, and you need to use a database. Commented Jan 2, 2015 at 19:28
  • 1
    A general programming tip for choosing arrays vs hashes. If you have to quickly access a specific value, use a hash, which acts like a random-access database. If you want to have a queue, or list, of values you'll sequentially access, then use an Array. So, for what you want, retrieving values tied to a specific IP, use a hash. An array, or array-of-arrays would cause the code to waste time looking for the particular value and would slow down as new items were added to the array because of those lookups. Commented Jan 3, 2015 at 17:58

3 Answers 3

3

In Ruby, multidimensional arrays are simply arrays of arrays:

bannedips = [["10.10.10.10", "more data", "etc"], ["20.20.20.20", ...]]

A better approach would be to use an array of hashes, so you can label values:

bannedips = [{ip: "10.10.10.10", timestamp: 89327414}, ...]
Sign up to request clarification or add additional context in comments.

Comments

2

If there are a reasonable number of IPs to be tracked, I'd probably use a simple Hash:

banned_ips = {
  "10.10.10.10" => {:timestamp => Time.now, :reason => 'foo'},
  "20.20.20.20" => {:timestamp => Time.now, :reason => 'bar'},
  "30.30.30.30" => {:timestamp => Time.now, :reason => nil}
}

A hash is a quick and dirty way to create a list that acts like an indexed database; Lookups are extremely fast. And, since you can only have a single instance of a particular key, it keeps you from dealing with duplicate data:

banned_ips["20.20.20.20"] # => {:timestamp=>2015-01-02 12:33:19 -0700, :reason=>"bar"}
banned_ips.keys # => ["10.10.10.10", "20.20.20.20", "30.30.30.30"]

As a general programming tip for choosing arrays vs hashes. If you:

  • have to quickly access a specific value, use a hash, which acts like a random-access database.
  • want to have a queue or list of values you'll sequentially access, then use an Array.

So, for what you want, retrieving values tied to a specific IP, use a hash. An array, or array-of-arrays would cause the code to waste time looking for the particular value and would slow down as new items were added to the array because of those lookups.

There's a point where it becomes more sensible to store this sort of information into a database, and as a developer it's good to learn about them. They're one of many tools we need to have in our toolbox.

2 Comments

Thanks for the answer. I'm going with this since it'll always be a small array. So I can use banned_ips.include? ip to check if an IP address is banned but what can/should I use to retrieve the additional details?
@John, the Tin Man already answered your question. banned_ips["20.20.20.20"] gives you the details hash. To get a particular value from that hash just retrieve it via its key like so: banned_ips["20.20.20.20"][:reason]
2

Yes, multidimensional arrays are possible in Ruby. Arrays can contain any value, so a multidimensional array is just an array which contains other arrays:

banned_ips = [
  ["10.10.10.10", Date.new(2015, 1, 2),   "reason"],
  ["20.20.20.20", Date.new(2014, 12, 28), "reason"],
  ["30.30.30.30", Date.new(2014, 12, 29), "reason"],
]

Personally though I wouldn't recommend using a multidimensional array for this purpose. Instead, create a class which encapsulates information about the banned IP.

Simple example:

class BannedIP
  attr_reader :ip, :time, :reason

  def initialize(ip, time:, reason: "N/A")
    @ip = ip
    @time = time
    @reason = reason
  end
end

banned_ips = [
  BannedIP.new("10.10.10.10", time: Date.new(2015, 1, 2)),
  BannedIP.new("20.20.20.20", time: Date.new(2014, 12, 28)),
  BannedIP.new("30.30.30.30", time: Date.new(2014, 12, 29), reason: "Spam"),
]    

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.