naturally 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -2
- data/README.md +52 -2
- data/lib/naturally.rb +2 -7
- data/lib/naturally/segment.rb +19 -4
- data/lib/naturally/version.rb +2 -1
- data/naturally.gemspec +1 -1
- data/spec/naturally_spec.rb +41 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a5b55ef725bab4abf9e87b176f86ab6c94d1ea4d
|
4
|
+
data.tar.gz: 2ca443c6027ed127aa570dceb2bdf63aeb060457
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85d8b14a18c0415b1360ce03a0eabad8e0cdfa07656648d71d251ac9556194aa705ff547b7cf5c6143762e876f425c4c05369047bef7368bdadaff24ce6d3705
|
7
|
+
data.tar.gz: df7f7705855241de8b0b1f4b26a4996398b90bfc5631088e5bcc9a141d2f98bc149a6192e9a19360b825839106ec06b3c12f586c327736b378eccb3f5c96694a
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -4,6 +4,11 @@
|
|
4
4
|
Natural (version number) sorting with support for **legal document numbering**, **college course codes**, and **Unicode**.
|
5
5
|
See Jeff Atwood's [Sorting for Humans: Natural Sort Order](http://www.codinghorror.com/blog/2007/12/sorting-for-humans-natural-sort-order.html) and the WebLaws.org post [Counting to 10 in Californian](http://www.weblaws.org/blog/2012/08/counting-from-1-to-10-in-californian/).
|
6
6
|
|
7
|
+
##Installation
|
8
|
+
|
9
|
+
```Shell
|
10
|
+
$ gem install naturally
|
11
|
+
```
|
7
12
|
|
8
13
|
## Usage
|
9
14
|
|
@@ -11,7 +16,7 @@ See Jeff Atwood's [Sorting for Humans: Natural Sort Order](http://www.codinghorr
|
|
11
16
|
require 'naturally'
|
12
17
|
|
13
18
|
# Sort a simple array of strings
|
14
|
-
Naturally.sort(["
|
19
|
+
Naturally.sort(["336", "335a", "335", "335.1"]) # => ["335", "335.1", "335a", "336"]
|
15
20
|
```
|
16
21
|
|
17
22
|
Usually the library is used to sort an array of objects:
|
@@ -45,7 +50,52 @@ expect(sorted.map(&:name)).to eq [
|
|
45
50
|
]
|
46
51
|
```
|
47
52
|
|
48
|
-
|
53
|
+
[More examples are in the specs](https://github.com/dogweather/naturally/blob/master/spec/naturally_spec.rb).
|
54
|
+
|
55
|
+
|
56
|
+
## Implementation Notes
|
57
|
+
|
58
|
+
The algorithm capitalizes on Ruby's array comparison behavior:
|
59
|
+
Since each dotted number actually represents a hierarchical
|
60
|
+
identifier, [array comparison](http://ruby-doc.org/core-2.2.1/Array.html#method-i-3C-3D-3E)
|
61
|
+
is a natural fit:
|
62
|
+
|
63
|
+
> Arrays are compared in an “element-wise” manner; the first element of ary is compared with the first one of other_ary using the <=> operator, then each of the second elements, etc… As soon as the result of any such comparison is non zero (i.e. the two corresponding elements are not equal), that result is returned for the whole array comparison.
|
64
|
+
|
65
|
+
|
66
|
+
And so, when given input such as,
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
['1.9', '1.9a', '1.10']
|
70
|
+
```
|
71
|
+
|
72
|
+
...this module sorts the segmented numbers
|
73
|
+
by comparing them in their array forms:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
[['1', '9'], ['1', '9a'], ['1', '10']]
|
77
|
+
```
|
78
|
+
|
79
|
+
Finally, upon actual sort comparison, each of these strings is
|
80
|
+
converted to an array of typed objects. This is to determine the
|
81
|
+
sort order between heterogenous (yet ordered) segments such as
|
82
|
+
`'9a'` and `'9'`.
|
83
|
+
|
84
|
+
The final nested comparison structure looks like this:
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
[
|
88
|
+
[
|
89
|
+
[1], [9]
|
90
|
+
],
|
91
|
+
[
|
92
|
+
[1], [9, 'a']
|
93
|
+
],
|
94
|
+
[
|
95
|
+
[1], [10]
|
96
|
+
]
|
97
|
+
]
|
98
|
+
```
|
49
99
|
|
50
100
|
## Related Work
|
51
101
|
|
data/lib/naturally.rb
CHANGED
@@ -2,11 +2,6 @@ require 'naturally/segment'
|
|
2
2
|
|
3
3
|
# A module which performs natural sorting on a variety of number
|
4
4
|
# formats. (See the specs for examples.)
|
5
|
-
#
|
6
|
-
# It achieves this by capitalizing on Ruby's behavior when
|
7
|
-
# comparing arrays: The module sorts arrays of segmented numbers such as
|
8
|
-
# ['1.9', '1.9a', '1.10'] by comparing them in their array forms.
|
9
|
-
# I.e., approximately [['1', '9'], ['1, '9a'], ['1', '10']]
|
10
5
|
module Naturally
|
11
6
|
# Perform a natural sort.
|
12
7
|
#
|
@@ -29,8 +24,8 @@ module Naturally
|
|
29
24
|
# This enables it to be sorted against other arrays
|
30
25
|
# by the standard #sort method.
|
31
26
|
#
|
32
|
-
# For example
|
33
|
-
#
|
27
|
+
# For example, '1.2a.3' becomes
|
28
|
+
# [Segment<'1'>, Segment<'2a'>, Segment<'3'>]
|
34
29
|
#
|
35
30
|
# @param [String] complex_number the number in a hierarchical form
|
36
31
|
# such as 1.2a.3.
|
data/lib/naturally/segment.rb
CHANGED
@@ -13,15 +13,30 @@ module Naturally
|
|
13
13
|
to_array <=> other.to_array
|
14
14
|
end
|
15
15
|
|
16
|
+
# @return [Array] a representation of myself in array form
|
17
|
+
# which enables me to be compared against
|
18
|
+
# another instance for sorting.
|
19
|
+
# The array is prepended with a symbol so
|
20
|
+
# two arrays are always comparable.
|
21
|
+
#
|
22
|
+
# @example a simple number
|
23
|
+
# Segment.new('10').to_array #=> [:int, 10]
|
24
|
+
#
|
25
|
+
# @example a college course code
|
26
|
+
# Segment.new('MATH101').to_array #=> [:str, "MATH", 101]
|
27
|
+
#
|
28
|
+
# @example Section 633a of the ADEA
|
29
|
+
# Segment.new('633a').to_array #=> [:int, 633, "a"]
|
16
30
|
def to_array
|
31
|
+
# TODO: Refactor, probably via polymorphism
|
17
32
|
if @val =~ /^(\p{Digit}+)(\p{Alpha}+)$/
|
18
|
-
[$1.to_i, $2]
|
33
|
+
[:int, $1.to_i, $2]
|
19
34
|
elsif @val =~ /^(\p{Alpha}+)(\p{Digit}+)$/
|
20
|
-
[$1, $2.to_i]
|
35
|
+
[:str, $1, $2.to_i]
|
21
36
|
elsif @val =~ /^\p{Digit}+$/
|
22
|
-
[@val.to_i]
|
37
|
+
[:int, @val.to_i]
|
23
38
|
else
|
24
|
-
[@val]
|
39
|
+
[:str, @val]
|
25
40
|
end
|
26
41
|
end
|
27
42
|
end
|
data/lib/naturally/version.rb
CHANGED
data/naturally.gemspec
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
1
|
lib = File.expand_path('../lib', __FILE__)
|
3
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
3
|
require 'naturally/version'
|
@@ -12,6 +11,7 @@ Gem::Specification.new do |gem|
|
|
12
11
|
gem.summary = %q{Sorts numbers according to the way people are used to seeing them.}
|
13
12
|
gem.description = %q{Natural Sorting with support for legal numbering, course numbers, and other number/letter mixes.}
|
14
13
|
gem.homepage = "http://github.com/dogweather/naturally"
|
14
|
+
gem.required_ruby_version = '>= 2.1'
|
15
15
|
|
16
16
|
gem.files = `git ls-files`.split($/)
|
17
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
data/spec/naturally_spec.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
1
|
require 'naturally'
|
3
2
|
|
4
3
|
describe Naturally do
|
@@ -63,6 +62,34 @@ describe Naturally do
|
|
63
62
|
to_this: %w(2 3 4 5 10 11 12 12а 12б 12в 13а 13б)
|
64
63
|
)
|
65
64
|
end
|
65
|
+
|
66
|
+
it 'sorts letters with digits correctly' do
|
67
|
+
it_sorts(
|
68
|
+
this: %w(1 a 2 b 3 c),
|
69
|
+
to_this: %w(1 2 3 a b c)
|
70
|
+
)
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'sorts complex numbers with digits correctly' do
|
74
|
+
it_sorts(
|
75
|
+
this: %w(1 a 2 b 3 c 1.1 a.1 1.2 a.2 1.3 a.3 b.1 ),
|
76
|
+
to_this: %w(1 1.1 1.2 1.3 2 3 a a.1 a.2 a.3 b b.1 c)
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'sorts complex mixes of numbers and digits correctly' do
|
81
|
+
it_sorts(
|
82
|
+
this: %w( 1.a.1 1.1 ),
|
83
|
+
to_this: %w( 1.1 1.a.1 )
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'sorts complex mixes of numbers and digits correctly' do
|
88
|
+
it_sorts(
|
89
|
+
this: %w( 1a1 1aa aaa ),
|
90
|
+
to_this: %w( 1aa 1a1 aaa )
|
91
|
+
)
|
92
|
+
end
|
66
93
|
end
|
67
94
|
|
68
95
|
describe '#sort_naturally_by' do
|
@@ -109,5 +136,18 @@ describe Naturally do
|
|
109
136
|
Париж
|
110
137
|
)
|
111
138
|
end
|
139
|
+
|
140
|
+
it 'sorts by an attribute which contains product names' do
|
141
|
+
Product = Struct.new(:name)
|
142
|
+
objects = [
|
143
|
+
Product.new('2 awesome decks'),
|
144
|
+
Product.new('Awesome deck')
|
145
|
+
]
|
146
|
+
actual = Naturally.sort_by(objects, :name)
|
147
|
+
expect(actual.map(&:name)).to eq [
|
148
|
+
'2 awesome decks',
|
149
|
+
'Awesome deck'
|
150
|
+
]
|
151
|
+
end
|
112
152
|
end
|
113
153
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: naturally
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robb Shecter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Natural Sorting with support for legal numbering, course numbers, and
|
14
14
|
other number/letter mixes.
|
@@ -41,7 +41,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
41
41
|
requirements:
|
42
42
|
- - ">="
|
43
43
|
- !ruby/object:Gem::Version
|
44
|
-
version: '
|
44
|
+
version: '2.1'
|
45
45
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
46
|
requirements:
|
47
47
|
- - ">="
|
@@ -49,7 +49,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
49
|
version: '0'
|
50
50
|
requirements: []
|
51
51
|
rubyforge_project:
|
52
|
-
rubygems_version: 2.4.
|
52
|
+
rubygems_version: 2.4.8
|
53
53
|
signing_key:
|
54
54
|
specification_version: 4
|
55
55
|
summary: Sorts numbers according to the way people are used to seeing them.
|