Checking through Array subset - ruby

I have a challenge, im trying to write a method that takes in an array and returns the subset and permutation of twos, including the initial array. How do I check for particular patterns in the array. For example, given this array:
[a,b,c]
subset return will be:
[a,b,c,], [a,b], [b,c], [c,a]
and I also need to check if each subset contains a particular letter. Here's my code:
def conflict_free?(a)
return a.permutation(2).to_a
end

Here's how to get the subsets you're looking for:
def subsets(a)
2.upto(a.length).flat_map {|n| a.combination(n).to_a}
end
irb(main):023:0> subsets(["a", "b", "c"])
=> [["a", "b"], ["a", "c"], ["b", "c"], ["a", "b", "c"]]
Anything else you want, you'll have to edit your question and provide more detail.

Here is a very compact and fast solution :
def conflict(a)
a.combination(2).to_a << a
end
>> [["a", "b"], ["a", "c"], ["b", "c"], ["a", "b", "c"]]
If you did want the initial array at the beginning you sacrificing a fair bit of speed. Nevertheless the best way to do it :
def conflict(a)
temp = [a]
a.combination(2).each { |com| temp << com}
temp
end
>> [["a", "b", "c"], ["a", "b"], ["a", "c"], ["b", "c"]]
If the input is not 3 then this will work :
def conflict(a)
temp = []
2.upto(a.size-1) {|i| temp += a.combination(i).to_a}
temp << a
end
The initial array can be added at the beginning or end. Above it's at the end.

Related

Switch Round Robins/Americano Algorithm in Ruby

Just as part of my hobby, I'm try to build a scheduler where you enter N number of players in the system, and it will generate an array of combinations. For example
players = ['A','B','C','D','E']
players.combination(2).to_a
# ["A", "B"], ["A", "C"], ["A", "D"], ["A", "E"], ["B", "C"], ["B", "D"], ["B", "E"], ["C", "D"], ["C", "E"], ["D", "E"]]
Then I would like my generate_matches functions to take this array of pairs, and turn this into matches where teach pair only plays once AND players of course cannot play themselves. This should work on a minimum 4 players, and work for every number above 4 whether it's odd or even.
I thought of it to be a simple issue to solve, but I've been just in a rabbit hole.
See pseudocode & code below:
# select random team
# find team that does not contain one of the players
# create match between team 1 and team 2
# delete team 1 and team 2 from original array
# repeat until no team left
def generate_matches(player_combinations)
matches = []
while !player_combinations.empty?
team_1 = player_combinations.shift
# if team doesn't include any of the team 1 player, select them
filtered_teams = player_combinations.select do |team|
(team & team_1).empty?
end
team_2 = filtered_teams.pop
player_combinations.delete(team_2)
matches << [team_1, team_2]
end
matches
end
Expected Result:
[
[['C','B'], ['A','E']],
[['E','D'], ['B','A']],
[['A','C'], ['D','B']],
[['B','E'], ['C','D']],
[['D','A'], ['E','C']],
]
Actual Result:
[
[["A", "C"], ["D", "E"]],
[["C", "D"], ["B", "E"]],
[["A", "B"], ["C", "E"]],
[["B", "C"], ["A", "E"]]
]
This should result in equal amount of matches for players, however it doesn't. I'm really confused why. See results after running it a few times:
Key Represents Player
Value represents # games they played
{"A"=>4, "C"=>3, "B"=>2, "E"=>4, "D"=>3}
But it should actually be:
{"A"=>4, "C"=>4, "B"=>4, "E"=>4, "D"=>4}
Sometimes it's just dumb luck, and I do get the correct answer. Most of the times, I don't. I guess I think too simple about this problem

Trying to AND all elements in a list of lists

I'm trying to create a new list of elements by ANDing the lists in my list of lists.
I've tried putting the list in a while loop with a counter representing the length of the list, and doing
values = values[counter] && values [counter + 1]
but for some reason this doesn't give me the correct result
my goal is this in a nutshell:
values = [["B", "W"],["C","W"]]
...
result = ["W"]
[["B", "W"], ["C", "W"]].reduce([], :&)
#=> ["W"]
[["B", "W", "A"], ["A", "C", "W"], ["W", "E", "A"]].reduce([], :&)
#=> ["W", "A"]
See Enumerable#reduce (aka inject) and Array#&. arr.reduce([], :&) is shorthand for:
arr.reduce([]) { |intersection, a| intersection & a }
reduce is assigned an initial value [] in case its receiver is an empty array.

Sort_by Ruby, One Descending, One Ascending

I have searched for an answer on this to no avail, there is a similar question but the answer did not work in this situation, it sorts on a numeric item. Similar Question -That did not work I am trying to use ruby's sort_by to sort one item descending with the other ascending. All I can find is one or the other.
Here is the code:
# Primary sort Last Name Descending, with ties broken by sorting Area of interest.
people = people.sort_by { |a| [ a.last_name, a.area_interest]}
Any guidance would certainly assist.
Sample data:
input
Russell, Logic
Euler, Graph Theory
Galois, Abstract Algebra
Gauss, Number Theory
Turing, Algorithms
Galois, Logic
output
Turing , Algorithms
Russell, Logic
Gauss, Number Theory
Galois, Abstract Algebra
Galois, Logic
Euler, Graph Theory
This is a straightforward way:
a = [ ['Russell', 'Logic'], ['Euler', 'Graph Theory'],
['Galois', 'Abstract Algebra'], ['Gauss', 'Number Theory'],
['Turing', 'Algorithms'], ['Galois', 'Logic'] ]
a.sort { |(name1,field1),(name2,field2)|
(name1 == name2) ? field1 <=> field2 : name2 <=> name1 }
#=> [ ["Turing", "Algorithms"], ["Russell", "Logic"],
# ["Gauss", "Number Theory"], ["Galois", "Abstract Algebra"],
# ["Galois", "Logic"], ["Euler", "Graph Theory"] ]
For multiple fields, sorting on the first in descending order, then on each of the others, in sequence, in ascending order:
a = [ %w{a b c}, %w{b a d}, %w{a b d}, %w{b c a}, %w{a b c}, %w{b c b}]
#=> [["a", "b", "c"], ["b", "a", "d"], ["a", "b", "d"],
# ["b", "c", "a"], ["a", "b", "c"], ["b", "c", "b"]]
a.sort { |e,f| e.first == f.first ? e[1..-1] <=> f[1..-1] : f <=> e }
#=> [["b", "a", "d"], ["b", "c", "a"], ["b", "c", "b"],
# ["a", "b", "c"], ["a", "b", "c"], ["a", "b", "d"]]
Make a custom class that invert the result of <=> (including Comparable).
Wrap the object you want sort descending with the custom class.
Example:
class Descending
include Comparable
attr :obj
def initialize(obj)
#obj = obj
end
def <=>(other)
return -(self.obj <=> other.obj)
end
end
people = [
{last_name: 'Russell', area_interest: 'Logic'},
{last_name: 'Euler', area_interest: 'Graph Theory'},
{last_name: 'Galois', area_interest: 'Abstract Algebra'},
{last_name: 'Gauss', area_interest: 'Number Theory'},
{last_name: 'Turing', area_interest: 'Algorithms'},
{last_name: 'Galois', area_interest: 'Logic'},
]
puts people.sort_by {|person| [
Descending.new(person[:last_name]), # <---------
person[:area_interest],
]}
output:
{:last_name=>"Turing", :area_interest=>"Algorithms"}
{:last_name=>"Russell", :area_interest=>"Logic"}
{:last_name=>"Gauss", :area_interest=>"Number Theory"}
{:last_name=>"Galois", :area_interest=>"Abstract Algebra"}
{:last_name=>"Galois", :area_interest=>"Logic"}
{:last_name=>"Euler", :area_interest=>"Graph Theory"}
BTW, if the object you want sort descending is a numeric value, you can simply use unary operator -:
people.sort_by {|person| [-person.age, person.name] }

delete similar elements in an array in ruby

I have the following array
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"]]
I want to get the following output [["a","b"], ["a","c"], ["b","c"]] and delete ["b","a"] and ["c","b"]
I have the following code
a3.each do |ary3|
x = ary3[0]
y = ary3[1]
x = ary3[1]
y = ary3[0]
if a3.include?([x,y])
a3 - [y,x]
end
end
print a3
I tried using the swap, but no luck!
Thanks for the help.
Two arrays are considered to be equal if they contain the same elements and these elements are in the same order:
["a", "b"] == ["b", "a"]
#=> false
["a", "b"] == ["a", "b"]
#=> true
So you need to sort the inner arrays first and then you can use Array#uniq to ensure that each element in the outer array will only appear once:
arr = [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
arr.map(&:sort).uniq
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
This will leave arr untouched, however:
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
You will need to use mutator methods (with a !) to edit the array in place:
arr = [["a", "b"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
arr.map!(&:sort).uniq!
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
Edit
As a follow-up to #sawa's comment, who was concerned that it may not be desirable to change the ordering of the inner arrays, i looked a bit deeper into Array#uniq. Consider the following array:
arr = [["b", "a"], ["a", "c"], ["b", "c"], ["b", "a"], ["c", "b"]]
I figured out that Array#uniq actually takes a block that lets you specify how the elements should be be compared:
arr.uniq!{|x| x.sort }
arr
#=> [["b", "a"], ["a", "c"], ["b", "c"]]
Cool thing is, this also works with Symbol#to_proc (the &: notation) and actually looks even more elegant than my original answer:
arr.uniq!(&:sort)
arr
#=> [["b", "a"], ["a", "c"], ["b", "c"]]
You can still use Array#sort! if you want the inner arrays to be sorted afterwards:
arr.uniq!(&:sort!)
arr
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
My last observation on this is though, that the order probably isn't important or else two arrays with different order would not be considered equal. This got me thinking (again) and i posed myself the question: Why not use a Set? It would work like this:
require 'set'
sets = [Set["a", "b"], Set["a", "c"], Set["b", "c"], Set["b", "a"], Set["c", "b"]]
sets.uniq!
sets
#=> [#<Set: {"a", "b"}>, #<Set: {"a", "c"}>, #<Set: {"b", "c"}>]
Just keep in mind that a Set will not allow you to add the same element multiple times, whereas an array does:
[%w[a b b b c], %w[a b b b c], %w[a b c]].uniq(&:sort)
#=> [["a", "b", "b", "b", "c"], ["a", "b", "c"]]
[Set.new(%w[a b b b c]), Set.new(%w[a b b b c]), Set.new(%w[a b c])].uniq
#=> [#<Set: {"a", "b", "c"}>]
check this one: #delete_if as below:
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"]]
p a3.delete_if{|x| [["b", "a"], ["c","b"]].include? x}
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
As per your comment and description post :
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
p a3.each {|x| a3.delete(x.reverse) if a3.include? x.reverse}
#=> [["a", "b"], ["a", "c"], ["b", "c"]]
BenchMark:
require 'benchmark'
N = 10000
Benchmark.bm(20) do | x |
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
x.report('Mine') do
N.times { a3.each {|x| a3.delete(x.reverse) if a3.include? x.reverse} }
end
a3 = [["a", "b"], ["a","c"], ["b","c"], ["b", "a"], ["c","b"],["b", "a"]]
x.report('padde') do
N.times { a3.uniq!(&:sort!) }
end
end
Output:
user system total real
Mine 0.172000 0.000000 0.172000 ( 0.361021)
padde 0.203000 0.000000 0.203000 ( 0.460026)

Ruby Combinations with array elements

Ok, i've searched the internet for answers and also searched for hours in my ruby programmer but i cant sort this out. I'm writing a script for making all sorts of combinations from elements in an array.
ar = ["a","b","c","d"]
At this point I am able to make these combinations:
["a"],["a","b"],["a","b","c"],["a","b","c","d"],["b"],["b","c"],["b","c","d"],["c"],["c","d"],["d"]
This is OK, but I can't find a way for searching these combinations, for example ["a","c"] or ["a","c","d"] or ["a","d"], etc...
For now my code looks like:
def combinaties(array)
combinaties = []
i=0
while i <= array.length-1
combinaties << array[i]
unless i == array.length-1
array[(i+1)..(array.length-1)].each{|volgend_element|
combinaties<<(combinaties.last.dup<<volgend_element)
}
end
i+=1
end
end
Functional approach (needs Ruby >= 1.9) to create the powerset of an array (except for the empty element you don't seem to need):
xs = ["a", "b", "c", "d"]
yss = 1.upto(xs.size).flat_map do |n|
xs.combination(n).to_a
end
#[
# ["a"], ["b"], ["c"], ["d"],
# ["a", "b"], ["a", "c"], ["a", "d"], ["b", "c"], ["b", "d"], ["c", "d"],
# ["a", "b", "c"], ["a", "b", "d"], ["a", "c", "d"], ["b", "c", "d"],
# ["a", "b", "c", "d"],
#]
There is a trivial correspondence (bijection) between such combinations and the numbers in [1..(2^m - 1)] (m being the array length).
Consider such a number n. It's binary representation has m digits (including leading zeros). The positions of the digits that are 1 are the indices of the elements in the corresponding combination.
The code would be:
def combinations(array)
m = array.length
(1...2**m).map do | n |
(0...m).select { | i | n[i] == 1 }.map { | i | array[i] }
end
end
Or in ruby 1.9
%w(a b c d e).combination(3).to_a
will give you all the combinations of size 3.

Resources