আমি কীভাবে দুটি হ্যাশ তুলনা করব?


108

আমি নিম্নলিখিত কোড ব্যবহার করে দুটি রুবি হ্যাশ তুলনা করার চেষ্টা করছি:

#!/usr/bin/env ruby

require "yaml"
require "active_support"

file1 = YAML::load(File.open('./en_20110207.yml'))
file2 = YAML::load(File.open('./locales/en.yml'))

arr = []

file1.select { |k,v|
  file2.select { |k2, v2|
    arr << "#{v2}" if "#{v}" != "#{v2}"
  }
}

puts arr

স্ক্রিনের আউটপুট হ'ল ফাইল 2 থেকে সম্পূর্ণ ফাইল। আমি ফাইলগুলি পৃথক যে সত্য জন্য জানি, কিন্তু স্ক্রিপ্ট এটি নিতে বলে মনে হয় না।


উত্তর:


161

আপনি সাম্যের জন্য সরাসরি হ্যাশ তুলনা করতে পারেন:

hash1 = {'a' => 1, 'b' => 2}
hash2 = {'a' => 1, 'b' => 2}
hash3 = {'a' => 1, 'b' => 2, 'c' => 3}

hash1 == hash2 # => true
hash1 == hash3 # => false

hash1.to_a == hash2.to_a # => true
hash1.to_a == hash3.to_a # => false


আপনি হ্যাশগুলিকে অ্যারেতে রূপান্তর করতে পারেন, তারপরে তাদের পার্থক্যটি পান:

hash3.to_a - hash1.to_a # => [["c", 3]]

if (hash3.size > hash1.size)
  difference = hash3.to_a - hash1.to_a
else
  difference = hash1.to_a - hash3.to_a
end
Hash[*difference.flatten] # => {"c"=>3}

আরও সরলকরণ:

একটি ত্রৈমাসিক কাঠামোর মাধ্যমে পার্থক্য নির্ধারণ:

  difference = (hash3.size > hash1.size) \
                ? hash3.to_a - hash1.to_a \
                : hash1.to_a - hash3.to_a
=> [["c", 3]]
  Hash[*difference.flatten] 
=> {"c"=>3}

এক ক্রিয়াকলাপে এগুলি করা এবং differenceভেরিয়েবল থেকে মুক্তি পাওয়া :

  Hash[*(
  (hash3.size > hash1.size)    \
      ? hash3.to_a - hash1.to_a \
      : hash1.to_a - hash3.to_a
  ).flatten] 
=> {"c"=>3}

3
উভয়ের মধ্যে পার্থক্য পেতে কি যাইহোক আছে?
dennismonsewicz

5
হ্যাশগুলি একই আকারের হতে পারে তবে বিভিন্ন মান থাকতে পারে। এই ক্ষেত্রে উভয় hash1.to_a - hash3.to_aএবং hash3.to_a - hash1.to_aঅদম্য মান যদিও ফিরে আসতে পারে hash1.size == hash3.sizeইডিআইটির পরে অংশটি হ্যাশগুলি বিভিন্ন আকারের হলেই বৈধ।
ওহালেক

3
ভাল, তবে এগিয়ে থাকাকালীন উচিত ছিল। এ.সাইজ> বি.সাইজের অগত্যা এ-এর অর্থ বি এর অন্তর্ভুক্ত নয় Still তবুও প্রতিসাম্যগত পার্থক্যগুলির ইউনিয়ন নেওয়া দরকার।
জিন

সরাসরি আউটপুটটির সাথে তুলনা .to_aকরতে ব্যর্থ হবে যখন সমান হ্যাশগুলির একটি পৃথক ক্রমে চাবি থাকে: {a:1, b:2} == {b:2, a:1}=> সত্য, {a:1, b:2}.to_a == {b:2, a:1}.to_a=> মিথ্যা
এইডান

এর উদ্দেশ্য কী flattenএবং *? শুধু কেন নয় Hash[A.to_a - B.to_a]?
জেরেমিকুন

34

আপনি হ্যাশডিফ মণির চেষ্টা করতে পারেন , যা হ্যাশের মধ্যে হ্যাশ এবং অ্যারের গভীর তুলনা করতে সহায়তা করে।

নিচেরটি একটি উদাহরণ:

a = {a:{x:2, y:3, z:4}, b:{x:3, z:45}}
b = {a:{y:3}, b:{y:3, z:30}}

diff = HashDiff.diff(a, b)
diff.should == [['-', 'a.x', 2], ['-', 'a.z', 4], ['-', 'b.x', 3], ['~', 'b.z', 45, 30], ['+', 'b.y', 3]]

4
আমার পরীক্ষার ব্যর্থতার কারণ হিসাবে বেশ কয়েকটি গভীর হ্যাশ ছিল। প্রতিস্থাপন got_hash.should eql expected_hashসঙ্গে HashDiff.diff(got_hash, expected_hash).should eql []আমি এখন আউটপুট যা শো আমি ঠিক কি প্রয়োজন পেতে। পারফেক্ট!
ডেভটাপলে

বাহ, হ্যাশডিফ দুর্দান্ত। বিশাল নেস্টেড জেএসওন অ্যারেতে কী পরিবর্তন হয়েছে তা দেখার চেষ্টা করার দ্রুত কাজ করে। ধন্যবাদ!
জেফ উইগাল

আপনার মণি দুর্দান্ত! JSON ম্যানিপুলেশনগুলির সাথে জড়িত স্পেসগুলি লেখার ক্ষেত্রে দুর্দান্ত সহায়ক। ধন্যবাদ.
আলাইন

2
হ্যাশডিফের সাথে আমার অভিজ্ঞতাটি হ'ল এটি ছোট হ্যাশগুলির জন্য সত্যই ভাল কাজ করে তবে ভিন্ন গতিটি ভাল স্কেল হয় না বলে মনে হয়। আপনার কলগুলি বেঞ্চমার্কিংয়ের পক্ষে যদি আপনি আশা করেন যে এটি দুটি বৃহত হ্যাশ খাওয়ানো হতে পারে এবং তা নিশ্চিত করা যায় যে পৃথক সময়টি আপনার সহনশীলতার মধ্যে রয়েছে।
ডেভিড বোডো

use_lcs: falseপতাকাটি ব্যবহার করে বড় হ্যাশগুলিতে তুলনা উল্লেখযোগ্যভাবে বাড়ানো যেতে পারে:Hashdiff.diff(b, a, use_lcs: false)
এরিক ওয়াকার

15

আপনি যদি দুটি হ্যাশের মধ্যে পার্থক্যটি পেতে চান তবে আপনি এটি করতে পারেন:

h1 = {:a => 20, :b => 10, :c => 44}
h2 = {:a => 2, :b => 10, :c => "44"}
result = {}
h1.each {|k, v| result[k] = h2[k] if h2[k] != v }
p result #=> {:a => 2, :c => "44"}

12

রেলগুলি পদ্ধতিটিকে হ্রাস করছে diff

দ্রুত ওয়ান-লাইনারের জন্য:

hash1.to_s == hash2.to_s

আমি সবসময় এই সম্পর্কে ভুলে যাই। প্রচুর পরিমাণে সাম্যতার চেক রয়েছে যা ব্যবহার করে সহজ করে দেওয়া হয়েছে to_s
টিন ম্যান

17
এটি ব্যর্থ হবে যখন সমান হ্যাশগুলির একটি আলাদা ক্রমে কী থাকে: {a:1, b:2} == {b:2, a:1}=> সত্য, {a:1, b:2}.to_s == {b:2, a:1}.to_s=> মিথ্যা
এইডান

2
কোন বৈশিষ্ট্য! : ডি
ডেভ মোর্স

5

আপনি একটি সাধারণ অ্যারে ছেদ ব্যবহার করতে পারেন, প্রতিটি হ্যাশের মধ্যে কী কী আলাদা তা আপনি জানতে পারবেন।

    hash1 = { a: 1 , b: 2 }
    hash2 = { a: 2 , b: 2 }

    overlapping_elements = hash1.to_a & hash2.to_a

    exclusive_elements_from_hash1 = hash1.to_a - overlapping_elements
    exclusive_elements_from_hash2 = hash2.to_a - overlapping_elements


1

আপনার যদি হ্যাশগুলির মধ্যে একটি দ্রুত এবং নোংরা পার্থক্য প্রয়োজন যা সঠিকভাবে মানগুলিকে শূন্য করে তবে আপনি এর মতো কিছু ব্যবহার করতে পারেন

def diff(one, other)
  (one.keys + other.keys).uniq.inject({}) do |memo, key|
    unless one.key?(key) && other.key?(key) && one[key] == other[key]
      memo[key] = [one.key?(key) ? one[key] : :_no_key, other.key?(key) ? other[key] : :_no_key]
    end
    memo
  end
end

1

আপনি যদি সুন্দর বিন্যাসিত ভিন্নতা চান তবে আপনি এটি করতে পারেন:

# Gemfile
gem 'awesome_print' # or gem install awesome_print

এবং আপনার কোডে:

require 'ap'

def my_diff(a, b)
  as = a.ai(plain: true).split("\n").map(&:strip)
  bs = b.ai(plain: true).split("\n").map(&:strip)
  ((as - bs) + (bs - as)).join("\n")
end

puts my_diff({foo: :bar, nested: {val1: 1, val2: 2}, end: :v},
             {foo: :bar, n2: {nested: {val1: 1, val2: 3}}, end: :v})

ধারণাটি হ'ল দুর্দান্ত ফর্ম্যাটটি ফর্ম্যাট করতে, এবং আউটপুটকে আলাদা করতে। তফাতটি সঠিক হবে না, তবে এটি ডিবাগিং উদ্দেশ্যে কার্যকর।


1

... এবং এখন মডিউল আকারে বিভিন্ন সংগ্রহের ক্লাসে প্রয়োগ করতে হবে (তাদের মধ্যে হ্যাশ)। এটি কোনও গভীর পরিদর্শন নয়, তবে এটি সহজ।

# Enable "diffing" and two-way transformations between collection objects
module Diffable
  # Calculates the changes required to transform self to the given collection.
  # @param b [Enumerable] The other collection object
  # @return [Array] The Diff: A two-element change set representing items to exclude and items to include
  def diff( b )
    a, b = to_a, b.to_a
    [a - b, b - a]
  end

  # Consume return value of Diffable#diff to produce a collection equal to the one used to produce the given diff.
  # @param to_drop [Enumerable] items to exclude from the target collection
  # @param to_add  [Enumerable] items to include in the target collection
  # @return [Array] New transformed collection equal to the one used to create the given change set
  def apply_diff( to_drop, to_add )
    to_a - to_drop + to_add
  end
end

if __FILE__ == $0
  # Demo: Hashes with overlapping keys and somewhat random values.
  Hash.send :include, Diffable
  rng = Random.new
  a = (:a..:q).to_a.reduce(Hash[]){|h,k| h.merge! Hash[k, rng.rand(2)] }
  b = (:i..:z).to_a.reduce(Hash[]){|h,k| h.merge! Hash[k, rng.rand(2)] }
  raise unless a == Hash[ b.apply_diff(*b.diff(a)) ] # change b to a
  raise unless b == Hash[ a.apply_diff(*a.diff(b)) ] # change a to b
  raise unless a == Hash[ a.apply_diff(*a.diff(a)) ] # change a to a
  raise unless b == Hash[ b.apply_diff(*b.diff(b)) ] # change b to b
end

1

দুটি হ্যাশ সমান হলে তুলনা করতে আমি এটি তৈরি করেছি

def hash_equal?(hash1, hash2)
  array1 = hash1.to_a
  array2 = hash2.to_a
  (array1 - array2 | array2 - array1) == []
end

ব্যবহার:

> hash_equal?({a: 4}, {a: 4})
=> true
> hash_equal?({a: 4}, {b: 4})
=> false

> hash_equal?({a: {b: 3}}, {a: {b: 3}})
=> true
> hash_equal?({a: {b: 3}}, {a: {b: 4}})
=> false

> hash_equal?({a: {b: {c: {d: {e: {f: {g: {h: 1}}}}}}}}, {a: {b: {c: {d: {e: {f: {g: {h: 1}}}}}}}})
=> true
> hash_equal?({a: {b: {c: {d: {e: {f: {g: {marino: 1}}}}}}}}, {a: {b: {c: {d: {e: {f: {g: {h: 2}}}}}}}})
=> false

0

" রুবি হ্যাশ তুলনা করা " এ এর ​​উত্তর দেওয়া হয়েছিল । রেলগুলি হ্যাশগুলিতে একটি diffপদ্ধতি যুক্ত করে। এটা ভাল কাজ করে.


5
পরিবর্তন পদ্ধতি পাগল সংস্করণ v4.0.2 চেয়ে নতুন থেকে শুরু অসমর্থিত হয়েছে।
আন্দ্রেস এহরনপেইসিস

0

উভয় হ্যাশ to_json রূপান্তর এবং স্ট্রিং হিসাবে তুলনা সম্পর্কে? তবে সেটা মাথায় রেখে

require "json"
h1 = {a: 20}
h2 = {a: "20"}

h1.to_json==h1.to_json
=> true
h1.to_json==h2.to_json
=> false

0

দুটি হ্যাশকে গভীরভাবে তুলনা করার জন্য এখানে অ্যালগরিদম রয়েছে, যা নেস্টেড অ্যারেগুলির সাথে তুলনা করবে:

    HashDiff.new(
      {val: 1, nested: [{a:1}, {b: [1, 2]}] },
      {val: 2, nested: [{a:1}, {b: [1]}] }
    ).report
# Output:
val:
- 1
+ 2
nested > 1 > b > 1:
- 2

বাস্তবায়ন:

class HashDiff

  attr_reader :left, :right

  def initialize(left, right, config = {}, path = nil)
    @left  = left
    @right = right
    @config = config
    @path = path
    @conformity = 0
  end

  def conformity
    find_differences
    @conformity
  end

  def report
    @config[:report] = true
    find_differences
  end

  def find_differences
    if hash?(left) && hash?(right)
      compare_hashes_keys
    elsif left.is_a?(Array) && right.is_a?(Array)
      compare_arrays
    else
      report_diff
    end
  end

  def compare_hashes_keys
    combined_keys.each do |key|
      l = value_with_default(left, key)
      r = value_with_default(right, key)
      if l == r
        @conformity += 100
      else
        compare_sub_items l, r, key
      end
    end
  end

  private

  def compare_sub_items(l, r, key)
    diff = self.class.new(l, r, @config, path(key))
    @conformity += diff.conformity
  end

  def report_diff
    return unless @config[:report]

    puts "#{@path}:"
    puts "- #{left}" unless left == NO_VALUE
    puts "+ #{right}" unless right == NO_VALUE
  end

  def combined_keys
    (left.keys + right.keys).uniq
  end

  def hash?(value)
    value.is_a?(Hash)
  end

  def compare_arrays
    l, r = left.clone, right.clone
    l.each_with_index do |l_item, l_index|
      max_item_index = nil
      max_conformity = 0
      r.each_with_index do |r_item, i|
        if l_item == r_item
          @conformity += 1
          r[i] = TAKEN
          break
        end

        diff = self.class.new(l_item, r_item, {})
        c = diff.conformity
        if c > max_conformity
          max_conformity = c
          max_item_index = i
        end
      end or next

      if max_item_index
        key = l_index == max_item_index ? l_index : "#{l_index}/#{max_item_index}"
        compare_sub_items l_item, r[max_item_index], key
        r[max_item_index] = TAKEN
      else
        compare_sub_items l_item, NO_VALUE, l_index
      end
    end

    r.each_with_index do |item, index|
      compare_sub_items NO_VALUE, item, index unless item == TAKEN
    end
  end

  def path(key)
    p = "#{@path} > " if @path
    "#{p}#{key}"
  end

  def value_with_default(obj, key)
    obj.fetch(key, NO_VALUE)
  end

  module NO_VALUE; end
  module TAKEN; end

end

-3

অন্য সম্পর্কে, সহজ পদ্ধতির সম্পর্কে:

require 'fileutils'
FileUtils.cmp(file1, file2)

2
আপনার কেবলমাত্র ডিস্কে হ্যাশগুলি অভিন্ন হওয়ার প্রয়োজন হলে এটি কেবল অর্থবহ। দুটি ফাইল যা ডিস্কে পৃথক কারণ হ্যাশ উপাদানগুলি বিভিন্ন ক্রমে থাকে, এখনও একই উপাদানগুলি ধারণ করতে পারে এবং রুবি লোড হওয়ার পরে যতটা উদাসীন হবে সমান হবে।
টিন ম্যান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.