2

ライブデータからダミーデータセットを作成するために、Rubyを使用していくつかの数値データをスクランブル(またはマスク)する簡単な方法を考え出そうとしています。データをできるだけ元の形式に近づけたい(つまり、数字以外のすべての文字を保持したい)。データ内の番号は、個々の識別番号に対応しています。これは、(場合によっては)リレーショナルデータベースで使用されるキーです。したがって、数値文字列が複数回出現する場合は、同じ(理想的には一意の)値に一貫してマップしたいと思います。データがスクランブリングされたら、スクランブリングを元に戻すことができる必要はありません。

文字列を受け取り、数値を新しい値にマップするための単純なハッシュを生成するスクランブル関数を作成しました(この関数は数字のみをマップし、その他はすべてそのままにします)。セキュリティを強化するために、関数が呼び出されるたびに、キーが再生成されます。したがって、同じフレーズを使用すると、関数が呼び出されるたびに2つの異なる結果が生成されます。

module HashModule
  def self.scramble(str)
    numHash ={}
    0.upto(9) do |i|
      numHash[i.to_s]=rand(10).to_s
    end

    output= String.new(str)
    output.gsub!(/\d/) do|d|
      d.replace numHash[d]
    end

    puts "Input: " + str
    puts "Hash Key: " + numHash.to_s
    puts "Output: " + output
  end
end

HashModule.scramble("56609-8 NO PCT 001")
HashModule.scramble("56609-8 NO PCT 001")

これにより、次の出力が生成されます。

Input: 56609-8 NO PCT 001
Hash Key: {"0"=>"9", "1"=>"4", "2"=>"8", 
           "3"=>"9", "4"=>"4", "5"=>"8", 
           "6"=>"4", "7"=>"0", "8"=>"2", 
           "9"=>"1"}
Output: 84491-2 NO PCT 994

Input: 56609-8 NO PCT 001
Hash Key: {"0"=>"2", "1"=>"0", "2"=>"9", 
           "3"=>"8", "4"=>"4", "5"=>"5", 
           "6"=>"7", "7"=>"4", "8"=>"2", 
           "9"=>"0"}
Output: 57720-2 NO PCT 220

与えられたデータセット:

PTO NO PC
R5632893423 IP
R566788882-001
NO PCT AMB PTO
NO AMB/CALL IP
A566788882
1655543AACHM IP
56664320000000
00566333-1

まず、すべての数値を配列に抽出します。次に、作成したスクランブル関数を使用して、置換ハッシュマップを作成します。

 {"5632893423"=>"5467106076", "566788882"=>"888299995", 
  "001"=>"225", "1655543"=>"2466605", 
  "56664320000000"=>"70007629999999", 
  "00566333"=>"00699999", "1"=>"3"}

[ちなみに、私の例では、ハッシュ値がすべて一意であると主張する方法が見つかりませんでした。これは、上記のように、マッピングされる文字列がリレーショナルデータベースの一意のIDに対応する場合に関連します。]

元の文字列でgsubを使用し、ハッシュキーをスクランブルされた値に置き換えます。私が持っているコードは機能しますが、どうすればもっと簡潔にすることができるか知りたいです。関数が呼び出されるたびにキーを再生成することで、余分な作業を作成していることに気付きます。(それ以外の場合は、1つのキーを作成してすべての数字を置き換えることができます)。

誰かが私がこれを別の方法で達成する方法についての提案がありますか?(私はRubyを初めて使用するので、コードを改善するための提案も大いに受け入れられます)。

input = <<EOS
PTO NO PC
R5632893423 IP
R566788882-001
NO PCT AMB PTO
NO AMB/CALL IP
A566788882
1655543AACHM IP
56664320000000
00566333-1
EOS

module HashModule
  def self.scramble(str)
    numHash ={}
    0.upto(9) do |i|
      numHash[i.to_s]=rand(10).to_s
    end

    output= String.new(str)
    output.gsub!(/\d/) do|d|
      d.replace numHash[d]
    end
    return output
  end
end

# Extract unique non-null numbers from the input file
numbers = input.split(/[^\d]/).uniq.reject{ |e| e.empty? }

# Create a hash that maps each number to a scrambled value
# Using the function defined above

mapper ={}
numbers.map(&:to_s).each {|x| mapper[x]=HashModule.scramble(x)}

# Create a regexp to find all numbers in input file
re = Regexp.new(mapper.keys.map { |x| Regexp.escape(x) }.join('|'))

# Replace numbers with scrambled values
puts input.gsub(re, mapper)

上記のコードは、次の出力を生成します。

PTO NO PC
R7834913043 IP
R799922223-772
NO PCT AMB PTO
NO AMB/CALL IP
A799922223
6955509AACHM IP
13330271111111
66166777-6
4

2 に答える 2

1

素晴らしい@sawaの答えに加えて、このスクランブルメソッドをStringクラスに直接「注入」することをお勧めします(追加のカーテシーを削除せずにstr.scrambleプロジェクト全体で利用できるようにします)。

class String
  @@ScrambleKey = Hash[(0..9).map(&:to_s).zip((0..9).to_a.shuffle)]
  def scramble ; self.gsub(/\d/) { @@ScrambleKey [$&] } end
end

この実装では、インスタンス変数ではなく、クラス変数が導入されます。ScrambleKey文字列ごとに異なるものが必要な場合は、代わりにインスタンス変数を使用してください。

降伏:

input = <<EOS
PTO NO PC
R5632893423 IP
R566788882-001
NO PCT AMB PTO
NO AMB/CALL IP
A566788882
1655543AACHM IP
56664320000000
00566333-1
EOS

puts input.scramble

与える:

PTO NO PC
R1548024784 IP
R155600008-339
NO PCT AMB PTO
NO AMB/CALL IP
A155600008
9511174AACHM IP
15557483333333
33155444-9
于 2013-02-11T09:27:41.500 に答える
1

多分このようなもの:

module HashModule
  ScrambleKey = Hash[(0..9).map(&:to_s).zip((0..9).to_a.shuffle)]
  def self.scramble(str); str.gsub(/\d/){ScrambleKey[$&]} end
end

puts HashModule.scramble(input)

これは次のようになります。

PTO NO PC
R6907580170 IP
R699455557-223
NO PCT AMB PTO
NO AMB/CALL IP
A699455557
3966610AACHM IP
69991072222222
22699000-3
于 2013-02-11T08:50:27.877 に答える