ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
From: duerst@it.aoyama.ac.jp
To: ruby-core@ruby-lang.org
Subject: [ruby-core:72363] [Ruby trunk - Feature #11815] Proposal for method `Array#difference`
Date: Fri, 18 Dec 2015 10:17:39 +0000	[thread overview]
Message-ID: <redmine.journal-55656.20151218101738.a22d0fc945f62d0e@ruby-lang.org> (raw)
In-Reply-To: redmine.issue-11815.20151214082709@ruby-lang.org

Issue #11815 has been updated by Martin Dürst.


Ryan Beltran wrote:

> I think I have a pretty good example. I'm implementing a function in Ruby that finds triples in an array for use in a pokerbot (recognizes if a hand is a triple). I already defined a function to check for doubles (which is relatively trivial to implement by comparing the original array to array.uniq), but triples are a little bit harder. There are several ways I could implement checking for triples, but a concise and efficient option would be to simply call:
> 
> `getDoubles(array.difference(array.uniq))` 

> 
> ~~~ ruby
> doubles = array.difference(array.uniq)
> triples = doubles.difference(doubles)
> fours   = triples.difference(triples)
> ~~~

An even more straightforward way is to use group_by:

~~~ ruby
n_tuples = array.group_by {|e| e}.values.group_by(&:length)

doubles = n_tuples[2]
triples = n_tuples[3]
fours   = n_tuples[4]
# and so on
~~~ ruby

We need group_by two times because the first one groups items with the same value, and the second organizes these by numbers. As an example, if we start with [1,2,2,3,3,3,4,4,4,4,5,5,5,7,8,8] then after the first group_by, we have
{1=>[1], 2=>[2, 2], 3=>[3, 3, 3], 4=>[4, 4, 4, 4], 5=>[5, 5, 5], 7=>[7], 8=>[8, 8]}.
Of this, we only need the values: [[1], [2, 2], [3, 3, 3], [4, 4, 4, 4], [5, 5, 5], [7], [8, 8]].
Then we group by length and get:
{1=>[[1], [7]], 2=>[[2, 2], [8, 8]], 3=>[[3, 3, 3], [5, 5, 5]], 4=>[[4, 4, 4, 4]]}
To get an unique value (i.e. 3 instead of [3, 3, 3]), just use .map(&:first).

----------------------------------------
Feature #11815: Proposal for method `Array#difference`
https://bugs.ruby-lang.org/issues/11815#change-55656

* Author: Cary Swoveland
* Status: Open
* Priority: Normal
* Assignee: 
----------------------------------------
I propose that a method `Array#difference` be added to the Ruby core. It is similar to [Array#-](http://ruby-doc.org/core-2.2.0/Array.html#method-i-2D) but for each element of the (array) argument it removes only one matching element from the receiver. For example:

    a = [1,2,3,4,3,2,2,4]
    b = [2,3,4,4,4]

    a - b #=> [1]
    c = a.difference b #=> [1, 3, 2, 2] 

As you see, `a` contains three `2`'s and `b` contains `1`, so the first `2` in `a` has been removed from `a` in constructing `c`. When `b` contains as least as many instances of an element as does `a`, `c` contains no instances of that element. 

It could be implemented as follows:

     class Array
       def difference(other)
         dup.tap do |cpy|
           other.each do |e|
             ndx = cpy.index(e)
             cpy.delete_at(ndx) if ndx
            end
          end
        end
      end

Here are a few examples of its use:

*Identify an array's unique elements*

      a = [1,3,2,4,3,4]
      u = a.uniq #=> [1, 2, 3, 4]
      u - a.difference(u) #=> [1, 2]

*Determine if two words of the same size are anagrams of each other*

      w1, w2 = "stop", "pots"
      w1.chars.difference(w2.chars).empty?
        #=> true

*Identify a maximal number of 1-1 matches between the elements of two arrays and return an array of all elements from both arrays that were not matched*

      a = [1, 2, 4, 2, 1, 7, 4, 2, 9] 
      b = [4, 7, 3, 2, 2, 7] 
      a.difference(b).concat(b.difference(a))
        #=> [1, 1, 4, 2, 9, 3, 7] 
  
To remove elements from `a` starting at the end (rather the beginning) of `a`:

    a = [1,2,3,4,3,2,2,4]
    b = [2,3,4,4,4]

    a.reverse.difference(b).reverse #=> [1,2,3,2]

`Array#difference!` could be defined in the obvious way.



-- 
https://bugs.ruby-lang.org/

  parent reply	other threads:[~2015-12-18  9:45 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <redmine.issue-11815.20151214082709@ruby-lang.org>
2015-12-14  8:27 ` [ruby-core:72107] [Ruby trunk - Feature #11815] [Open] Proposal for method `Array#difference` cary
2015-12-14  9:38 ` [ruby-core:72111] [Ruby trunk - Feature #11815] " cary
2015-12-15  4:33 ` [ruby-core:72132] " matz
2015-12-15  5:15 ` [ruby-core:72135] " cary
2015-12-15  6:56 ` [ruby-core:72138] " cary
2015-12-15  7:02 ` [ruby-core:72139] " cary
2015-12-15  8:01 ` [ruby-core:72140] " duerst
2015-12-15  8:47 ` [ruby-core:72141] " cary
2015-12-15 12:27 ` [ruby-core:72143] " 6ftdan
2015-12-15 17:37 ` [ruby-core:72151] " cary
2015-12-15 17:52 ` [ruby-core:72154] " 6ftdan
2015-12-18  3:48 ` [ruby-core:72235] " rp.beltran
2015-12-18 10:17 ` duerst [this message]
2015-12-19 19:22 ` [ruby-core:72392] " cary
2016-08-19 22:43 ` [ruby-core:76988] [Ruby trunk Feature#11815] " cary
2016-09-15 16:43 ` [ruby-core:77285] " cary
2018-09-29 14:14 ` [ruby-core:89215] " florian.ebeling
2018-09-30 12:16 ` [ruby-core:89219] " florian.ebeling
2018-10-05 20:04 ` [ruby-core:89290] " florian.ebeling

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-list from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.ruby-lang.org/en/community/mailing-lists/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=redmine.journal-55656.20151218101738.a22d0fc945f62d0e@ruby-lang.org \
    --to=ruby-core@ruby-lang.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).