[ACCEPTED]-diff a ruby string or array-diff

Accepted answer
Score: 35

For arrays, use the minus operator. For 7 example:

>> foo = [1, 2, 3]
=> [1, 2, 3]
>> goo = [2, 3, 4]
=> [2, 3, 4]
>> foo - goo
=> [1]

Here the last line removes everything 6 from foo that is also in goo, leaving just 5 the element 1. I don't know how to do this 4 for two strings, but until somebody who 3 knows posts about it, you could just convert 2 each string to an array, use the minus operator, and 1 then convert the result back.

Score: 24

I got frustrated with the lack of a good 4 library for this in ruby, so I wrote http://github.com/samg/diffy. It 3 uses diff under the covers, and focuses on being 2 convenient, and providing pretty output 1 options.

Score: 21

For strings, I would first try out the Ruby 2 Gem that @sam-saffron mentioned below. It's 1 easier to install: http://github.com/pvande/differ/tree/master

gem install differ

irb
require 'differ'

one = "one two three"
two = "one two 3"

Differ.format = :color
puts Differ.diff_by_word(one, two).to_s

Differ.format = :html
puts Differ.diff_by_word(one, two).to_s
Score: 5

There is also diff-lcs which is available as a gem. It hasn't been updated since 2004 but we 3 have been using it without any problem.

Edit: A 2 new version was released in 2011. Looks 1 like it's back in active development.

http://rubygems.org/gems/diff-lcs

Score: 5

The HTMLDiff that @da01 mentions above worked 2 for me.

script/plugin install git://github.com/myobie/htmldiff.git

# bottom of environment.rb
require 'htmldiff'

# in model
class Page < ActiveRecord::Base
  extend HTMLDiff
end

# in view
<h1>Revisions for <%= @page.name %></h1>
<ul>
<% @page.revisions.each do |revision| %>
  <li>
    <b>Revised <%= distance_of_time_in_words_to_now revision.created_at %> ago</b><BR>
      <%= Page.diff(
        revision.changes['description'][0],
        revision.changes['description'][1]
      ) %>
      <BR><BR>
  </li>
<% end %>

# in style.css
ins.diffmod, ins.diffins { background: #d4fdd5; text-decoration: none; }
del.diffmod, del.diffdel { color: #ff9999; }

Looks pretty good. By the way 1 I used this with the acts_as_audited plugin.

Score: 5
t=s2.chars; s1.chars.map{|c| c == t.shift ? c : '^'}.join

This simple line gives a ^ in the positions 2 that don't match. That's often enough and 1 it's copy/paste-able.

Score: 2

I just found a new project that seems pretty 2 flexible:

http://github.com/pvande/differ/tree/master

Trying it out and will try to 1 post some sort of report.

Score: 2

I had the same doubt and the solution I 5 found is not 100% ruby, but is the best 4 for me. The problem with diff.rb is that 3 it doesn't have a pretty formatter, to show 2 the diffs in a humanized way. So I used 1 diff from the OS with this code:

 def diff str1, str2
   system "diff #{file_for str1} #{file_for str2}"
 end

 private
 def file_for text
   exp = Tempfile.new("bk", "/tmp").open
   exp.write(text)
   exp.close
   exp.path
 end
Score: 2

Just for the benefit of Windows people: diffy 4 looks brilliant but I belive it will only 3 work on *nix (correct me if I'm wrong). Certainly 2 it didn't work on my machine.

Differ worked 1 a treat for me (Windows 7 x64, Ruby 1.8.7).

Score: 1
Score: 1

To get character by character resolution 1 I added a new function to damerau-levenshtein gem

require "damerau-levenshtein"
differ = DamerauLevenshtein::Differ.new
differ.run "Something", "Smothing"
# returns ["S<ins>o</ins>m<subst>e</subst>thing", 
#  "S<del>o</del>m<subst>o</subst>thing"]

or with parsing:

require "damerau-levenshtein"
require "nokogiri"

differ = DamerauLevenshtein::Differ.new
res = differ.run("Something", "Smothing!")
nodes = Nokogiri::XML("<root>#{res.first}</root>")

markup = nodes.root.children.map do |n|
  case n.name
  when "text"
    n.text
  when "del"
    "~~#{n.children.first.text}~~"
  when "ins"
    "*#{n.children.first.text}*"
  when "subst"
    "**#{n.children.first.text}**"
  end
end.join("")

puts markup

More Related questions