mirror of
https://github.com/hkalexling/Mango.git
synced 2026-03-17 00:03:36 -04:00
113 lines
2.5 KiB
Crystal
113 lines
2.5 KiB
Crystal
# Helper method used to sort chapters in a folder
|
|
# It respects the keywords like "Vol." and "Ch." in the filenames
|
|
# This sorting method was initially implemented in JS and done in the frontend.
|
|
# see https://github.com/hkalexling/Mango/blob/
|
|
# 07100121ef15260b5a8e8da0e5948c993df574c5/public/js/sort-items.js#L15-L87
|
|
|
|
require "big"
|
|
|
|
private class Item
|
|
getter numbers : Hash(String, BigDecimal)
|
|
|
|
def initialize(@numbers)
|
|
end
|
|
|
|
# Compare with another Item using keys
|
|
def <=>(other : Item, keys : Array(String))
|
|
keys.each do |key|
|
|
if !@numbers.has_key?(key) && !other.numbers.has_key?(key)
|
|
next
|
|
elsif !@numbers.has_key? key
|
|
return 1
|
|
elsif !other.numbers.has_key? key
|
|
return -1
|
|
elsif @numbers[key] == other.numbers[key]
|
|
next
|
|
else
|
|
return @numbers[key] <=> other.numbers[key]
|
|
end
|
|
end
|
|
|
|
0
|
|
end
|
|
end
|
|
|
|
private class KeyRange
|
|
getter min : BigDecimal, max : BigDecimal, count : Int32
|
|
|
|
def initialize(value : BigDecimal)
|
|
@min = @max = value
|
|
@count = 1
|
|
end
|
|
|
|
def update(value : BigDecimal)
|
|
@min = value if value < @min
|
|
@max = value if value > @max
|
|
@count += 1
|
|
end
|
|
|
|
def range
|
|
@max - @min
|
|
end
|
|
end
|
|
|
|
class ChapterSorter
|
|
@sorted_keys = [] of String
|
|
|
|
def initialize(str_ary : Array(String))
|
|
keys = {} of String => KeyRange
|
|
|
|
str_ary.each do |str|
|
|
scan str do |k, v|
|
|
if keys.has_key? k
|
|
keys[k].update v
|
|
else
|
|
keys[k] = KeyRange.new v
|
|
end
|
|
end
|
|
end
|
|
|
|
# Get the array of keys string and sort them
|
|
@sorted_keys = keys.keys
|
|
# Only use keys that are present in over half of the strings
|
|
.select do |key|
|
|
keys[key].count >= str_ary.size / 2
|
|
end
|
|
.sort! do |a_key, b_key|
|
|
a = keys[a_key]
|
|
b = keys[b_key]
|
|
# Sort keys by the number of times they appear
|
|
count_compare = b.count <=> a.count
|
|
if count_compare == 0
|
|
# Then sort by value range
|
|
b.range <=> a.range
|
|
else
|
|
count_compare
|
|
end
|
|
end
|
|
end
|
|
|
|
def compare(a : String, b : String)
|
|
item_a = str_to_item a
|
|
item_b = str_to_item b
|
|
item_a.<=>(item_b, @sorted_keys)
|
|
end
|
|
|
|
private def scan(str, &)
|
|
str.scan /([^0-9\n\r\ ]*)[ ]*([0-9]*\.*[0-9]+)/ do |match|
|
|
key = match[1]
|
|
num = match[2].to_big_d
|
|
|
|
yield key, num
|
|
end
|
|
end
|
|
|
|
private def str_to_item(str)
|
|
numbers = {} of String => BigDecimal
|
|
scan str do |k, v|
|
|
numbers[k] = v
|
|
end
|
|
Item.new numbers
|
|
end
|
|
end
|