diff a ruby string or array

RubyDiff

Ruby Problem Overview


How do I do a diff of two strings or arrays in Ruby?

Ruby Solutions


Solution 1 - Ruby

For arrays, use the minus operator. For example:

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

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

Solution 2 - Ruby

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

Solution 3 - Ruby

Solution 4 - Ruby

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

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

[1]: http://github.com/myobie/htmldiff/tree/master "HTMLDiff on GitHub" [2]: http://github.com/pvande/differ/tree/master

Solution 5 - Ruby

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

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

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

Solution 6 - Ruby

The HTMLDiff that @da01 mentions above worked 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 I used this with the acts_as_audited plugin.

Solution 7 - Ruby

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

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

Solution 8 - Ruby

I just found a new project that seems pretty flexible:

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

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

Solution 9 - Ruby

I had the same doubt and the solution I found is not 100% ruby, but is the best for me. The problem with diff.rb is that it doesn't have a pretty formatter, to show the diffs in a humanized way. So I used 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

Solution 10 - Ruby

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

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

Solution 11 - Ruby

Solution 12 - Ruby

To get character by character resolution 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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJosh MooreView Question on Stackoverflow
Solution 1 - RubyChris BunchView Answer on Stackoverflow
Solution 2 - RubysamgView Answer on Stackoverflow
Solution 3 - RubyBrian MitchellView Answer on Stackoverflow
Solution 4 - Rubyda01View Answer on Stackoverflow
Solution 5 - RubyGrant HutchinsView Answer on Stackoverflow
Solution 6 - RubyBrian ArmstrongView Answer on Stackoverflow
Solution 7 - RubySteveView Answer on Stackoverflow
Solution 8 - RubySam SaffronView Answer on Stackoverflow
Solution 9 - RubyDaniel CukierView Answer on Stackoverflow
Solution 10 - RubyrussthegibbonView Answer on Stackoverflow
Solution 11 - RubygrosserView Answer on Stackoverflow
Solution 12 - RubydimusView Answer on Stackoverflow