How do I do a diff of two strings or arrays in Ruby?
-
I have reworded this question in the hope that it can now be reopened. I think the question was valid, just worded in a way that made it sound like it would result in a lot of opinion answers. If you want to help reopen this, you should see a tiny "reopen" link just under the "ruby" and "diff" question tags. – Gerry Jun 30 '16 at 23:38
-
this is about the 10th question that i've found this week that has been flagged as off topic. it's not off topic. it's a great question. please please, hall monitors, stop doing this. – user1130176 Feb 16 '17 at 11:34
-
For strings you can use this answer http://stackoverflow.com/a/42573286/745489 – Dzmitry Mar 03 '17 at 07:44
12 Answers
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.

- 87,773
- 37
- 126
- 127
-
2This is incorrect. foo = [1,2,3] bar = [4,5,6] foo - bar # => [1,2,3] Ruby's implementation is correct, but I don't thin this is what the question asked for. – Chris Lloyd Aug 24 '10 at 06:59
-
6To get around Chris L's point, you can do (foo - bar) + (bar - foo). To get all differences between the arrays (or strings if you use .to_a) – diedthreetimes Jul 21 '11 at 00:05
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.

- 3,496
- 1
- 25
- 26
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
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
diff.rb is what you want, which is available at http://users.cybercity.dk/~dsl8950/ruby/diff.html via internet archive:
http://web.archive.org/web/20140421214841/http://users.cybercity.dk:80/~dsl8950/ruby/diff.html

- 43,168
- 21
- 122
- 188

- 2,280
- 14
- 12
-
22For those that don't grok the implications of GPL: it includes a 'copyleft' concept which demands that any proprietary code you incorporate it into forces you to open source and GPL-give-away your previously-proprietary code. – New Alexandria Apr 25 '14 at 15:21
-
2Commenting to say that link is still dead http://www.downforeveryoneorjustme.com/http://users.cybercity.dk/~dsl8950/ruby/diff.html – Btuman May 09 '17 at 18:39
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.

- 4,275
- 1
- 27
- 32
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.

- 877
- 1
- 11
- 20

- 61
- 1
- 2
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.

- 19,707
- 17
- 115
- 144
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.

- 128,308
- 78
- 326
- 506
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

- 11,502
- 15
- 68
- 123
-
1using temporary files is generally a bad idea when you have an in memory option available. – epochwolf Oct 15 '09 at 19:15
-
1@epochwolf How are you making diff read from memory? It only supports diffing files as far as I'm aware. – Gerry Apr 03 '16 at 01:17
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).

- 303
- 2
- 8
-
There is some Windows support baked in now. There's some details about what kind of setup you need to do in the README. https://github.com/samg/diffy#on-windows – samg Dec 28 '12 at 07:23
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

- 8,712
- 10
- 45
- 56
Maybe Array.diff via monkey-patch helps...
http://grosser.it/2011/07/07/ruby-array-diffother-difference-between-2-arrays/

- 14,707
- 7
- 57
- 61