Sort_by Ruby, One Descending, One Ascending - ruby

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] }

Related

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.

How to join every X amount of characters together in an Array - Ruby

If I want to join every X amount of letters together in an array how could I implement this?
In this case I want to join every two letters together
Input: array = ["b", "i", "e", "t", "r", "o"]
Output: array = ["bi", "et", "ro"]
each_slice (docs):
arr = 'bietro'.split ''
# grab each slice of 2 elements
p arr.each_slice(2).to_a
#=> [["b", "i"], ["e", "t"], ["r", "o"]]
# map `join' over each of the slices
p arr.each_slice(2).map(&:join)
#=> ["bi", "et", "ro"]
#Doorknow shows the best way, but here are two (among many, many) other ways:
def bunch_em(arr,n)
((arr.size+n-1)/n).times.map { |i| arr.slice(i*n,n).join }
end
arr = ["b", "i", "e", "t", "r", "o"]
bunch_em(arr,2) #=> ["bi", "et", "ro"]

Cartesian product of array with itself without duplicates in Ruby

How can I do a cartesian product of an array with itself, without including both [object i, object j] and [object j, object i]?
Currently, I've got
array = %w{a b c}
unique_combinations = array.each_with_index.to_a.product(array.each_with_index.to_a).
find_all{|(first_object, i), (second_object, j)| i < j}.
map{|(first_object, i), (second_object, j)| [first_object, second_object]}
unique_combinations # => [["a", "b"], ["a", "c"], ["b", "c"]]
which works, but feels a little verbose.
I could do
array = %w{a b c}
combinations = array.product(array)
unique_combinations = combinations.find_all{|first_item, second_item| array.index(first_item) < array.index(second_item)}
but that feels like I'm throwing information away, and would only work if the array only had unique items in it.
Another approach is
unique_combinations = []
array.each_with_index do |first_item, i|
array.each_with_index do |second_item, j|
next unless i < j
unique_combinations << [first_item, second_item]
end
end
but that feels too imperative rather than functional.
It's called a combination?
a = %w{a b c}
a.combination(2).to_a
=> [["a", "b"], ["a", "c"], ["b", "c"]]

Checking through Array subset

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.

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