true_table 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +5 -2
- data/lib/true_table.rb +236 -2
- metadata +2 -4
- data/lib/true_table/table.rb +0 -198
- data/lib/true_table/version.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53e26ebda846b40d7f2399e318b3be09ce49c71ddebbab97bfc34fbd64d664b4
|
4
|
+
data.tar.gz: 1427d9de17b647d15469522ea5483b268ca41ef82a325b286617dc72ada93ad9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b2aa85ebfd3c93611bcd499fa1c551dfac70b552353e89e60e0a68362763c9e8e5663d9557d310d36fb488409aaf3fac46234dc95fe18644422fc7654ec05155
|
7
|
+
data.tar.gz: 9c8d9423738e0724c3bbcac5fc93187c8bd8c2e68cd5cb8a481324bd0b2080b3087ff9dad498b024ec41f0784a061afc0988c5654f177073c2a1334ec9cb78e1
|
data/README.md
CHANGED
data/lib/true_table.rb
CHANGED
@@ -1,2 +1,236 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
class TrueTable < Array
|
4
|
+
class << self
|
5
|
+
# Loads a CSV string
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# csv = "id,name\n1,Harry\n2,Lloyd"
|
9
|
+
# table = TrueTable.load_csv csv, converters: [:date, :numeric]
|
10
|
+
#
|
11
|
+
# @param [String] csv_string CSV string.
|
12
|
+
# @param [Hash] options options to forward to the CSV class.
|
13
|
+
#
|
14
|
+
# @return [TrueTable] the new table object
|
15
|
+
def from_csv(csv_string, options = {})
|
16
|
+
default_options = { headers: true, converters: :numeric, header_converters: :symbol }
|
17
|
+
csv = CSV.new csv_string, **default_options.merge(options)
|
18
|
+
new.tap do |table|
|
19
|
+
csv.each { |row| table << row.to_h }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Loads a CSV file
|
24
|
+
#
|
25
|
+
# @example
|
26
|
+
# table = TrueTable.from_csv "sample.csv", converters: [:date, :numeric]
|
27
|
+
#
|
28
|
+
# @param [String] path the path to the CSV file.
|
29
|
+
# @param [Hash] options options to forward to the CSV class.
|
30
|
+
#
|
31
|
+
# @return [TrueTable] the new table object
|
32
|
+
def load_csv(path, options = {})
|
33
|
+
from_csv File.read(path), options
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Combines table with other and returns a new one
|
38
|
+
def +(other)
|
39
|
+
result = self.class.new
|
40
|
+
each_row { |row, i| result << row.merge(other[i]) }
|
41
|
+
result
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a new table without the specified columns
|
45
|
+
def -(cols)
|
46
|
+
keep_keys = headers - cols
|
47
|
+
result = self.class.new
|
48
|
+
each_row { |row, i| result << row.slice(*keep_keys) }
|
49
|
+
result
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns a row or a column
|
53
|
+
def [](key)
|
54
|
+
key.is_a?(Symbol) ? col(key) : super
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds or updates a row or a column
|
58
|
+
def []=(key, value)
|
59
|
+
key.is_a?(Symbol) ? add_col(key.to_sym, value) : super
|
60
|
+
end
|
61
|
+
|
62
|
+
# Returns a deep copy of self
|
63
|
+
def clone
|
64
|
+
self.class.new map(&:clone)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a column as Array
|
68
|
+
def col(key)
|
69
|
+
map { |row| row[key] }
|
70
|
+
end
|
71
|
+
|
72
|
+
# Returns a hash of columns
|
73
|
+
def cols
|
74
|
+
result = {}
|
75
|
+
each_col { |col, header| result[header] = col }
|
76
|
+
result
|
77
|
+
end
|
78
|
+
|
79
|
+
# Returns a copy of self without rows that contain nil in any column
|
80
|
+
def compact
|
81
|
+
dup.compact!
|
82
|
+
end
|
83
|
+
|
84
|
+
# Removes rows with nil in any column
|
85
|
+
def compact!
|
86
|
+
delete_if { |row| row.values.include? nil }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Delete a row or a column in place and returns the deleted row/column
|
90
|
+
def delete_at(index)
|
91
|
+
if index.is_a?(Symbol) || index.is_a?(String)
|
92
|
+
result = self[index]
|
93
|
+
return nil unless result
|
94
|
+
each_row { |row, i| row.delete index }
|
95
|
+
result
|
96
|
+
else
|
97
|
+
super
|
98
|
+
end
|
99
|
+
end
|
100
|
+
alias delete_col delete_at
|
101
|
+
alias delete_row delete_at
|
102
|
+
|
103
|
+
# Returns a table with different rows
|
104
|
+
def difference(*others)
|
105
|
+
self.class.new super
|
106
|
+
end
|
107
|
+
|
108
|
+
# Extracts nested value. Accepts row, column or column, row
|
109
|
+
def dig(*indexes)
|
110
|
+
key = indexes.shift
|
111
|
+
if key.is_a?(Symbol)
|
112
|
+
col(key.to_sym).dig *indexes
|
113
|
+
else
|
114
|
+
row(key).dig *indexes
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
# Iterates over columns
|
119
|
+
def each_col
|
120
|
+
headers.each { |header| yield col(header), header }
|
121
|
+
end
|
122
|
+
|
123
|
+
# Iterates over rows
|
124
|
+
alias each_row each_with_index
|
125
|
+
|
126
|
+
def fetch(key, *default)
|
127
|
+
if key.is_a?(Symbol)
|
128
|
+
if headers.include? key
|
129
|
+
col(key.to_sym)
|
130
|
+
elsif default.any?
|
131
|
+
default.first
|
132
|
+
elsif block_given?
|
133
|
+
yield key
|
134
|
+
else
|
135
|
+
raise IndexError, "row :#{key} does not exist"
|
136
|
+
end
|
137
|
+
else
|
138
|
+
super
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Returns an array of column headers
|
143
|
+
def headers
|
144
|
+
first.keys
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns a new table with intersecting rows
|
148
|
+
def intersection(*others)
|
149
|
+
self.class.new super
|
150
|
+
end
|
151
|
+
|
152
|
+
# Returns a string with joined rows and columns
|
153
|
+
def join(row_separator = $,, col_separator = nil, with_headers: false)
|
154
|
+
if col_separator
|
155
|
+
result = map { |row| row.values.join col_separator }.join(row_separator)
|
156
|
+
with_headers ? headers.join(col_separator) + row_separator + result : result
|
157
|
+
else
|
158
|
+
super row_separator
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns the last row or a new table with the last N rows
|
163
|
+
def last(*args)
|
164
|
+
args.empty? ? super : self.class.new(super)
|
165
|
+
end
|
166
|
+
|
167
|
+
# Returns a new table without rejected rows
|
168
|
+
def reject
|
169
|
+
self.class.new super
|
170
|
+
end
|
171
|
+
|
172
|
+
# Returns a reversed copy
|
173
|
+
def reverse
|
174
|
+
self.class.new super
|
175
|
+
end
|
176
|
+
|
177
|
+
# Returns a new table with the element at `count` as the first element
|
178
|
+
def rotate(count = 1)
|
179
|
+
self.class.new super
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns a row
|
183
|
+
alias row []
|
184
|
+
|
185
|
+
# Saves the table as a CSV file
|
186
|
+
def save_csv(path)
|
187
|
+
File.write path, to_csv
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns a new table with selected rows
|
191
|
+
def select
|
192
|
+
self.class.new super
|
193
|
+
end
|
194
|
+
alias filter select
|
195
|
+
|
196
|
+
# Returns a new shuffled tables
|
197
|
+
def shuffle(*args)
|
198
|
+
self.class.new super
|
199
|
+
end
|
200
|
+
|
201
|
+
# Returns a new sorted table
|
202
|
+
def sort
|
203
|
+
self.class.new super
|
204
|
+
end
|
205
|
+
|
206
|
+
# Returns a new sorted table
|
207
|
+
def sort_by
|
208
|
+
self.class.new super
|
209
|
+
end
|
210
|
+
|
211
|
+
# Returns a CSV string
|
212
|
+
def to_csv(row_separator = "\n", col_separator = ",")
|
213
|
+
join(row_separator, col_separator, with_headers: true)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns a hash representation of the table using the values of the
|
217
|
+
# first column as hash keys
|
218
|
+
def to_h
|
219
|
+
map { |row| [row.values.first, row] }.to_h
|
220
|
+
end
|
221
|
+
|
222
|
+
# Returns only values, without any headers (array of arrays)
|
223
|
+
def values
|
224
|
+
map { |row| row.values }
|
225
|
+
end
|
226
|
+
|
227
|
+
protected
|
228
|
+
|
229
|
+
def add_col(key, values)
|
230
|
+
values.each_with_index do |value, i|
|
231
|
+
self[i] ||= {}
|
232
|
+
self[i][key] = value
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: true_table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Danny Ben Shitrit
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Simple and intuitive tabular data type
|
14
14
|
email: db@dannyben.com
|
@@ -18,8 +18,6 @@ extra_rdoc_files: []
|
|
18
18
|
files:
|
19
19
|
- README.md
|
20
20
|
- lib/true_table.rb
|
21
|
-
- lib/true_table/table.rb
|
22
|
-
- lib/true_table/version.rb
|
23
21
|
homepage: https://github.com/dannyben/true_table
|
24
22
|
licenses:
|
25
23
|
- MIT
|
data/lib/true_table/table.rb
DELETED
@@ -1,198 +0,0 @@
|
|
1
|
-
module TrueTable
|
2
|
-
class Table < Array
|
3
|
-
# Combines table with other and returns a new one
|
4
|
-
def +(other)
|
5
|
-
result = self.class.new
|
6
|
-
each_row { |row, i| result << row.merge(other[i]) }
|
7
|
-
result
|
8
|
-
end
|
9
|
-
|
10
|
-
# Returns a new table without the specified columns
|
11
|
-
def -(cols)
|
12
|
-
keep_keys = headers - cols
|
13
|
-
result = self.class.new
|
14
|
-
each_row { |row, i| result << row.slice(*keep_keys) }
|
15
|
-
result
|
16
|
-
end
|
17
|
-
|
18
|
-
# Returns a row or a column
|
19
|
-
def [](key)
|
20
|
-
key.is_a?(Symbol) ? col(key) : super
|
21
|
-
end
|
22
|
-
|
23
|
-
# Adds or updates a row or a column
|
24
|
-
def []=(key, value)
|
25
|
-
key.is_a?(Symbol) ? add_col(key.to_sym, value) : super
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns a deep copy of self
|
29
|
-
def clone
|
30
|
-
self.class.new map(&:clone)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Returns a column as Array
|
34
|
-
def col(key)
|
35
|
-
map { |row| row[key] }
|
36
|
-
end
|
37
|
-
|
38
|
-
# Returns a hash of columns
|
39
|
-
def cols
|
40
|
-
result = {}
|
41
|
-
each_col { |col, header| result[header] = col }
|
42
|
-
result
|
43
|
-
end
|
44
|
-
|
45
|
-
# Returns a copy of self without rows that contain nil in any column
|
46
|
-
def compact
|
47
|
-
dup.compact!
|
48
|
-
end
|
49
|
-
|
50
|
-
# Removes rows with nil in any column
|
51
|
-
def compact!
|
52
|
-
delete_if { |row| row.values.include? nil }
|
53
|
-
end
|
54
|
-
|
55
|
-
# Delete a row or a column in place and returns the deleted row/column
|
56
|
-
def delete_at(index)
|
57
|
-
if index.is_a?(Symbol) || index.is_a?(String)
|
58
|
-
result = self[index]
|
59
|
-
return nil unless result
|
60
|
-
each_row { |row, i| row.delete index }
|
61
|
-
result
|
62
|
-
else
|
63
|
-
super
|
64
|
-
end
|
65
|
-
end
|
66
|
-
alias delete_col delete_at
|
67
|
-
alias delete_row delete_at
|
68
|
-
|
69
|
-
# Returns a table with different rows
|
70
|
-
def difference(*others)
|
71
|
-
self.class.new super
|
72
|
-
end
|
73
|
-
|
74
|
-
# Extracts nested value. Accepts row, column or column, row
|
75
|
-
def dig(*indexes)
|
76
|
-
key = indexes.shift
|
77
|
-
if key.is_a?(Symbol)
|
78
|
-
col(key.to_sym).dig *indexes
|
79
|
-
else
|
80
|
-
row(key).dig *indexes
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
# Iterates over columns
|
85
|
-
def each_col
|
86
|
-
headers.each { |header| yield col(header), header }
|
87
|
-
end
|
88
|
-
|
89
|
-
# Iterates over rows
|
90
|
-
alias each_row each_with_index
|
91
|
-
|
92
|
-
def fetch(key, *default)
|
93
|
-
if key.is_a?(Symbol)
|
94
|
-
if headers.include? key
|
95
|
-
col(key.to_sym)
|
96
|
-
elsif default.any?
|
97
|
-
default.first
|
98
|
-
elsif block_given?
|
99
|
-
yield key
|
100
|
-
else
|
101
|
-
raise IndexError, "row :#{key} does not exist"
|
102
|
-
end
|
103
|
-
else
|
104
|
-
super
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
# Returns an array of column headers
|
109
|
-
def headers
|
110
|
-
first.keys
|
111
|
-
end
|
112
|
-
|
113
|
-
# Returns a new table with intersecting rows
|
114
|
-
def intersection(*others)
|
115
|
-
self.class.new super
|
116
|
-
end
|
117
|
-
|
118
|
-
# Returns a string with joined rows and columns
|
119
|
-
def join(row_separator = $,, col_separator = nil, with_headers: false)
|
120
|
-
if col_separator
|
121
|
-
result = map { |row| row.values.join col_separator }.join(row_separator)
|
122
|
-
with_headers ? headers.join(col_separator) + row_separator + result : result
|
123
|
-
else
|
124
|
-
super row_separator
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns the last row or a new table with the last N rows
|
129
|
-
def last(*args)
|
130
|
-
args.empty? ? super : self.class.new(super)
|
131
|
-
end
|
132
|
-
|
133
|
-
# Returns a new table without rejected rows
|
134
|
-
def reject
|
135
|
-
self.class.new super
|
136
|
-
end
|
137
|
-
|
138
|
-
# Returns a reversed copy
|
139
|
-
def reverse
|
140
|
-
self.class.new super
|
141
|
-
end
|
142
|
-
|
143
|
-
# Returns a new table with the element at `count` as the first element
|
144
|
-
def rotate(count = 1)
|
145
|
-
self.class.new super
|
146
|
-
end
|
147
|
-
|
148
|
-
# Returns a row
|
149
|
-
alias row []
|
150
|
-
|
151
|
-
# Returns a new table with selected rows
|
152
|
-
def select
|
153
|
-
self.class.new super
|
154
|
-
end
|
155
|
-
alias filter select
|
156
|
-
|
157
|
-
# Returns a new shuffled tables
|
158
|
-
def shuffle(*args)
|
159
|
-
self.class.new super
|
160
|
-
end
|
161
|
-
|
162
|
-
# Returns a new sorted table
|
163
|
-
def sort
|
164
|
-
self.class.new super
|
165
|
-
end
|
166
|
-
|
167
|
-
# Returns a new sorted table
|
168
|
-
def sort_by
|
169
|
-
self.class.new super
|
170
|
-
end
|
171
|
-
|
172
|
-
# Returns a CSV string
|
173
|
-
def to_csv(row_separator = "\n", col_separator = ",")
|
174
|
-
join(row_separator, col_separator, with_headers: true)
|
175
|
-
end
|
176
|
-
|
177
|
-
# Returns a hash representation of the table using the values of the
|
178
|
-
# first column as hash keys
|
179
|
-
def to_h
|
180
|
-
map { |row| [row.values.first, row] }.to_h
|
181
|
-
end
|
182
|
-
|
183
|
-
# Returns only values, without any headers (array of arrays)
|
184
|
-
def values
|
185
|
-
map { |row| row.values }
|
186
|
-
end
|
187
|
-
|
188
|
-
protected
|
189
|
-
|
190
|
-
def add_col(key, values)
|
191
|
-
values.each_with_index do |value, i|
|
192
|
-
self[i] ||= {}
|
193
|
-
self[i][key] = value
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
end
|
198
|
-
end
|
data/lib/true_table/version.rb
DELETED