0

I have an array of objects "orders" which consists of books/readers/date

I need to get the top reader (is the one that takes the most number of books) and the quantity of returned top readers must be configurable. Default quantity is 1 reader

Here is an output from 16 orders

 [#<Order:0x0000562626492558 @book="Codename: American Man", @reader="Mariel Beier", @date="30.03.2021">, 
    #<Order:0x00005626264924e0 @book="Forbidden Wizard", @reader="Mariel Beier", @date="30.03.2021">,
     #<Order:0x0000562626492468 @book="Action Ninja", @reader="Taryn Gutmann", @date="30.03.2021">,
     #<Order:0x00005626264923f0 @book="Action Ninja", @reader="Garret Lindgren", @date="30.03.2021">, 
    #<Order:0x0000562626492378 @book="Forbidden Wizard", @reader="Alysa Keeling", @date="30.03.2021">, 
    #<Order:0x0000562626492300 @book="War of the American Imp", @reader="Garrett Stroman", @date="30.03.2021">, 
    #<Order:0x0000562626492288 @book="Blue Witch", @reader="Garrett Stroman", @date="30.03.2021">, 
    #<Order:0x0000562626492210 @book="War of the American Imp", @reader="Sherlyn Schumm", @date="30.03.2021">, 
    #<Order:0x0000562626492198 @book="Action Ninja", @reader="Mac Funk", @date="30.03.2021">, 
    #<Order:0x0000562626492120 @book="Forbidden Wizard", @reader="Mariel Beier", @date="30.03.2021">, 
    #<Order:0x00005626264920a8 @book="The Nuclear Wolves", @reader="Les Conn", @date="30.03.2021">, 
    #<Order:0x0000562626492030 @book="War of the American Imp", @reader="Taryn Gutmann", @date="30.03.2021">, 
    #<Order:0x0000562626491fb8 @book="War of the American Imp", @reader="Les Conn", @date="30.03.2021">, 
    #<Order:0x0000562626491f40 @book="The Nuclear Wolves", @reader="Les Conn", @date="30.03.2021">, 
    #<Order:0x0000562626491ec8 @book="Action Ninja", @reader="Diamond Cole", @date="30.03.2021">, 
    #<Order:0x0000562626491e50 @book="Action Ninja", @reader="Garret Lindgren", @date="30.03.2021">]

Basically i need to get the mode by keywords (books/readers/the most popular author)

I am using ffaker to generate random data for this array

  def build_author
    name = FFaker::Book.author
    biography = FFaker::Book.description
    Author.new(name, biography)
  end

  def build_book(author)
    title = FFaker::Book.title
    Book.new(title, author.name)
  end

  def build_reader
    name = FFaker::Name.name
    email = FFaker::Internet.email
    city = FFaker::Address.city
    street = FFaker::Address.street_name
    house = rand(1 - 10_000)
    Reader.new(name, email, city, street, house)
  end

  def build_order(book, reader)
    date = Time.now.utc.strftime('%d.%m.%Y')
    Order.new(book.title, reader.name, date)
  end

  def fill_author
    3.times do
      author = build_author
      @authors.push(author)
    end
  end

  def fill_book
    6.times do
      book = build_book(@authors.sample)
      @books.push(book)
    end
  end

  def fill_reader
    10.times do
      reader = build_reader
      @readers.push(reader)
    end
  end

  def fill_order
    16.times do
      @orders.push(build_order(@books.sample, @readers.sample))
    end
  end

Here is the main library class

class Library
  include DataBuilder
  include Statistics

  attr_accessor :authors, :books, :orders, :readers

  def initialize(authors: [], books: [], orders: [], readers: [])
    @books = books
    @orders = orders
    @readers = readers
    @authors = authors
  end

  def create_data
    create_arrays
    generate_data
  end

  def show
    create_data
    p top_reader
  end

Edit:

There is how i get top reader/book now

  def top_reader
    orders_grouped = orders.group_by(&:reader)
    tab = orders_grouped.max_by { |_k, v| v.count }.first
    puts "#{tab} is the top reader"
  end

  def top_book
    orders_grouped = orders.group_by(&:book)
    tab = orders_grouped.max_by { |_k, v| v.count }.first
    puts "#{tab} is the top book"
  end

Here is an output:

Mariel Beier is the top reader
Action Ninja is the top book

Don't know yet how to configure the amount of top readers/books

2
  • What does the model/relations look like, can you post that code? Commented Mar 31, 2021 at 11:35
  • @Int'lManOfCodingMystery Man Of Coding Mystery updated. I just need to get the reader that purchases the biggest amount of books. And the most purchasable book Commented Mar 31, 2021 at 12:29

2 Answers 2

3

Sorry, I simplified a little to focus on the problem at hand (at least what I understood of it)

class Order
  attr_reader :book, :reader, :date
  def initialize(b,r,d)
    @book = b
    @reader = r
    @date = d
  end
end

orders = [
  Order.new("Codename: American Man", "Mariel Beier", "30.03.2021"),
  Order.new("Forbidden Wizard", "Mariel Beier", "30.03.2021"),
  Order.new("Action Ninja", "Taryn Gutmann", "30.03.2021"),
  Order.new("Action Ninja", "Garret Lindgren", "30.03.2021"),
  Order.new("Forbidden Wizard", "Alysa Keeling", "30.03.2021"),
  Order.new("War of the American Imp", "Garrett Stroman", "30.03.2021"),
  Order.new("Blue Witch", "Garrett Stroman", "30.03.2021"),
  Order.new("War of the American Imp", "Sherlyn Schumm", "30.03.2021"),
  Order.new("Action Ninja", "Mac Funk", "30.03.2021"),
  Order.new("Forbidden Wizard", "Mariel Beier", "30.03.2021"),
  Order.new("The Nuclear Wolves", "Les Conn", "30.03.2021"),
  Order.new("War of the American Imp", "Taryn Gutmann", "30.03.2021"),
  Order.new("War of the American Imp", "Les Conn", "30.03.2021"),
  Order.new("The Nuclear Wolves", "Les Conn", "30.03.2021"),
  Order.new("Action Ninja", "Diamond Cole", "30.03.2021"),
  Order.new("Action Ninja", "Garret Lindgren", "30.03.2021")
]

Edit: As Cary Swoveland pointed out, there's a waaay better way (than sorting the whole hash) to get a given number of "top rankers":

def top_rankers( orders, filter, count = 1)
  hash = Hash.new(0)
  orders.each do |order|
    hash[order.public_send(filter)] += 1
  end
  hash.max_by(count, &:last)
end

Or in a more concise way

def top_rankers( orders, filter, count = 1 )
  orders.each_with_object(Hash.new(0)) { |o,h| h[o.public_send(filter)] += 1 }.max_by(count, &:last)
end

Which gives you an easily manipulable result:

top_rankers(orders, :reader, 5)
# => [["Les Conn", 3], ["Mariel Beier", 3], ["Garret Lindgren", 2], ["Taryn Gutmann", 2], ["Garrett Stroman", 2]]

top_rankers(orders, :reader, 3).map(&:first)
# => ["Les Conn", "Mariel Beier", "Garrett Stroman"]

top_rankers(orders, :book)
# => [["Action Ninja", 5]]

top_rankers(orders, :book, 2).to_h
# => {"Action Ninja"=>5, "War of the American Imp"=>4}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks Carry, I never stop learning things with you :-) I didn't thought of Enumerable#max_by which fits the specs a lot better than Enumerable#sort_by
Let's say top_nbr_readers = 1, I'm not sure about what to do when there are multiple readers with the same "reading count", discard the unlucky ones ?
That depends on requirements. If all tied top readers were wanted, you'd need to get the number of books read by the top_nbr_readersth reader, say, N, then select all readers who've read at least N books. No need for attribution in your answer. More than a few times I've ditched my entire answer to replace it with a suggestion made by a commenter (thanking that person in a comment).
Thanks for help! Now i am getting top reader/book but don't know how to get top 2-3
@Danyil, you should contemplate the lines with # =>. What does my top_ranker function gives you ?
1

You can use a combination of group_by to group books for each reader, and sort_by to sort readers

  orders_grouped = orders.group_by { |order| order.reader }
  tab = orders_grouped.sort_by { |k,v| v.uniq.size }.reverse!

tab output using hash and three first orders

[["Mariel Beier", [{:book=>"Codename: American Man", :reader=>"Mariel Beier", :date=>"30.03.2021"}, {:book=>"Forbidden Wizard", :reader=>"Mariel Beier", :date=>"30.03.2021"}]],
 ["Taryn Gutmann", [{:book=>"Action Ninja", :reader=>"Taryn Gutmann", :date=>"30.03.2021"}]]]

Then you can take readers with quantity like this

tab[0..quantity-1]

3 Comments

Gives me the next output when trying to get top reader Invasion of the Ultra Woman was bought by Tiesha Blanda at 31.03.2021 Curse of the Forbidden Identity was bought by Tiesha Blanda at 31.03.2021 War of the Killer Cat was bought by Tiesha Blanda at 31.03.2021 I am Tokyo Thief was bought by Tiesha Blanda at 31.03.2021 . How do i get just the name of the reader?
access the first element of returned value to get the top reader
Thanks for help! Now i am getting top reader/book but don't know how to get top 2-3

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.