fun-ruby 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,559 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunRuby
4
+ # Module containing methods for hashes
5
+ module Hash
6
+ include Common::Helpers
7
+
8
+ extend self
9
+
10
+ # Returns a value stored by a given key.
11
+ # If there is no given key it return a default value
12
+ #
13
+ # @since 0.1.0
14
+ #
15
+ # @param key [#hash, #eql?]
16
+ # @param hash [#to_h]
17
+ #
18
+ # @return [Object]
19
+ #
20
+ # @example Base
21
+ # hash = { age: 20, name: 'John' }
22
+ # F::Hash.get(:age, hash) # => 20
23
+ # F::Hash.get(:name, hash) # => 'John'
24
+ # F::Hash.get(:email, hash) # => nil
25
+ #
26
+ # @example Curried
27
+ # hash = { age: 20, name: 'John' }
28
+ # curried = F::Hash.get
29
+ # curried.(:age, hash) # => 20
30
+ # curried.(:name).(hash) # => 'John'
31
+ #
32
+ # @example Curried
33
+ # hash = { age: 20, name: 'John' }
34
+ # curried = F::Hash.get
35
+ # curried.(:age, hash) # => 20
36
+ # curried.(:name).(hash) # => 'John'
37
+ #
38
+ # @example Curried with placeholders
39
+ # hash = { age: 20, name: 'John' }
40
+ # curried = F::Hash.get(F._, hash)
41
+ # curried.(:age) # => 20
42
+ # curried.(:name) # => 'John'
43
+ def get(key = F._, hash = F._)
44
+ curry_implementation(:get, key, hash)
45
+ end
46
+
47
+ # Returns a value stored by a given key.
48
+ # If there is no given key it raises error
49
+ #
50
+ # @since 0.1.0
51
+ #
52
+ # @param key [#hash, #eql?]
53
+ # @param hash [#to_h]
54
+ #
55
+ # @return [Object]
56
+ #
57
+ # @raise KeyError
58
+ #
59
+ # @example Base
60
+ # hash = { age: 20, name: 'John' }
61
+ # F::Hash.fetch(:age, hash) # => 20
62
+ # F::Hash.fetch(:name, hash) # => 'John'
63
+ # F::Hash.fetch(:email, hash) # => raise KeyError, "key not found: :email"
64
+ #
65
+ # @example Curried
66
+ # hash = { age: 20, name: 'John' }
67
+ # curried = F::Hash.fetch
68
+ # curried.(:age, hash) # => 20
69
+ # curried.(:name).(hash) # => 'John'
70
+ # curried.(:email).(hash) # => raise KeyError, "key not found: :email"
71
+ #
72
+ # @example Curried with placeholders
73
+ # hash = { age: 20, name: 'John' }
74
+ # curried = F::Hash.fetch(F._, hash)
75
+ # curried.(:age) # => 20
76
+ # curried.(:name) # => 'John'
77
+ def fetch(key = F._, hash = F._)
78
+ curry_implementation(:fetch, key, hash)
79
+ end
80
+
81
+ # Returns a value stored by a given key.
82
+ # If there is no given key it calls a given fallback.
83
+ #
84
+ # @since 0.1.0
85
+ #
86
+ # @param key [#hash, #eql?]
87
+ # @param fallback [#to_call]
88
+ # @param hash [#to_h]
89
+ #
90
+ # @return [Object]
91
+ #
92
+ # @example Base
93
+ # hash = { age: 20, name: 'John' }
94
+ # F::Hash.fetch_with(:age, ->(_) { :fallback }, hash) # => 20
95
+ # F::Hash.fetch_with(:name, ->(_) { :fallback }, hash) # => 'John'
96
+ # F::Hash.fetch_with(:email, ->(_) { :fallback }, hash) # => :fallback
97
+ #
98
+ # @example Curried
99
+ # hash = { age: 20, name: 'John' }
100
+ # curried = F::Hash.fetch_with
101
+ # curried.(:age, ->(_) { :fallback }, hash) # => 20
102
+ # curried.(:name).(->(_) { :fallback }).(hash) # => 'John'
103
+ # curried.(:email).(->(_) { :fallback }).(hash) # => :fallback
104
+ #
105
+ # @example Curried with placeholders
106
+ # hash = { age: 20, name: 'John' }
107
+ # curried = F::Hash.fetch_with(F._, F._, F._)
108
+ # curried.(:age).(->(_) { :fallback }).(hash) # => 20
109
+ #
110
+ # curried = F::Hash.fetch_with(F._, ->(_) { :fallback }, F._)
111
+ # curried.(:age).(hash) # => 20
112
+ # curried.(:email).(hash) # => :fallback
113
+ def fetch_with(key = F._, fallback = F._, hash = F._)
114
+ curry_implementation(:fetch_with, key, fallback, hash)
115
+ end
116
+
117
+ # Puts a value by a given key.
118
+ # If the key is already taken the current value will be replaced.
119
+ #
120
+ # @since 0.1.0
121
+ #
122
+ # @param key [#hash, #eql?]
123
+ # @param value [Object]
124
+ # @param hash [#to_h]
125
+ #
126
+ # @return [Hash]
127
+ #
128
+ # @example Base
129
+ # hash = { name: "John" }
130
+ # F::Hash.put(:age, 20, hash) # => { name: "John", age: 20 }
131
+ # F::Hash.put(:name, "Peter", hash) # => { name: "Peter" }
132
+ #
133
+ # @example Curried
134
+ # hash = { name: "John" }
135
+ # curried = F::Hash.put
136
+ # curried.(:age).(20).(hash) # => { name: "John", age: 20 }
137
+ # curried.(:name).("Peter").(hash) # => { name: "Peter" }
138
+ #
139
+ # @example Curried with placeholders
140
+ # hash = { name: "John" }
141
+ # curried = F::Hash.put(F._, 20, hash)
142
+ # curried.(:age) # => { name: "John", age: 20 }
143
+ #
144
+ # curried = F::Hash.put(F._, 20, F._)
145
+ # curried.(:age).(hash) # => { name: "John", age: 20 }
146
+ #
147
+ # curried = F::Hash.put(:age, F._, hash)
148
+ # curried.(20) # => { name: "John", age: 20 }
149
+ def put(key = F._, value = F._, hash = F._)
150
+ curry_implementation(:put, key, value, hash)
151
+ end
152
+
153
+ # Merges two hashes. The key-value pairs from the second to the first.
154
+ # If the key is already taken the current value will be replaced.
155
+ #
156
+ # @since 0.1.0
157
+ #
158
+ # @param first [#to_h]
159
+ # @param second [#to_h]
160
+ #
161
+ # @return [Hash]
162
+ #
163
+ # @example Base
164
+ # first = { name: "John" }
165
+ # second = { age: 20 }
166
+ # F::Hash.merge(first, second) # => { name: "John", age: 20 }
167
+ #
168
+ # first = { name: "John" }
169
+ # second = { name: "Bill", age: 20 }
170
+ # F::Hash.merge(first, second) # => { name: "Bill", age: 20 }
171
+ #
172
+ # @example Curried
173
+ # first = { name: "John" }
174
+ # second = { age: 20 }
175
+ # curried = F::Hash.merge
176
+ # curried.(first).(second) # => { name: "John", age: 20 }
177
+ #
178
+ # first = { name: "John" }
179
+ # second = { name: "Bill", age: 20 }
180
+ # curried = F::Hash.merge
181
+ # curried.(first).(second) # => { name: "Bill", age: 20 }
182
+ #
183
+ # @example Curried with placeholders
184
+ # first = { name: "John" }
185
+ # second = { age: 20 }
186
+ # curried = F::Hash.merge(F._, F._)
187
+ # curried.(first).(second) # => { name: "John", age: 20 }
188
+ #
189
+ # first = { name: "John" }
190
+ # second = { name: "Bill", age: 20 }
191
+ # curried = F::Hash.merge(F._, second)
192
+ # curried.(first) # => { name: "Bill", age: 20 }
193
+ def merge(first = F._, second = F._)
194
+ curry_implementation(:merge, first, second)
195
+ end
196
+
197
+ # Builds a new hash with only given keys from a given hash.
198
+ # If keys are missed in the given hash the corresponded key will be absent
199
+ # in the result hash.
200
+ #
201
+ # @since 0.1.0
202
+ #
203
+ # @param keys [::Array of (#hash, #eql?)]
204
+ # @param hash [#to_h]
205
+ #
206
+ # @return [Hash]
207
+ #
208
+ # @example Base
209
+ # hash = { name: "John", age: 20, email: "john@gmail.com", country: "USA" }
210
+ #
211
+ # F::Hash.slice([:name, :age, :country], hash) # => { name: "John", age: 20, country: "USA" }
212
+ # F::Hash.slice([:name, :age, :address], hash) # => { name: "John", age: 20 }
213
+ #
214
+ # @example Curried
215
+ # hash = { name: "John", age: 20, email: "john@gmail.com", country: "USA" }
216
+ # curried = F::Hash.slice
217
+ #
218
+ # curried.([:name, :age, :country]).(hash) # => { name: "John", age: 20, country: "USA" }
219
+ # curried.([:name, :age, :address]).(hash) # => { name: "John", age: 20 }
220
+ #
221
+ # @example Curried with placeholders
222
+ # hash = { name: "John", age: 20, email: "john@gmail.com", country: "USA" }
223
+ #
224
+ # curried = F::Hash.slice(F._, hash)
225
+ # curried.([:name, :age, :country]) # => { name: "John", age: 20, country: "USA" }
226
+ # curried.([:name, :age, :address]) # => { name: "John", age: 20 }
227
+ def slice(keys = F._, hash = F._)
228
+ curry_implementation(:slice, keys, hash)
229
+ end
230
+
231
+ # Builds a new hash with only given keys from a given hash.
232
+ # If keys are missed in the given hash KeyError is raised
233
+ #
234
+ # @since 0.1.0
235
+ #
236
+ # @param keys [::Array of (#hash, #eql?)]
237
+ # @param hash [#to_h]
238
+ #
239
+ # @return [Hash]
240
+ #
241
+ # @example Base
242
+ # hash = { name: "John", age: 20, country: "USA" }
243
+ #
244
+ # F::Hash.slice!([:name, :age, :country], hash) # => { name: "John", age: 20, country: "USA" }
245
+ # F::Hash.slice!([:address, :email], hash) # => raise KeyError, "keys not found: [:address, :email]"
246
+ def slice!(keys = F._, hash = F._)
247
+ curry_implementation(:slice!, keys, hash)
248
+ end
249
+
250
+ # Returns an array of values stored by given keys
251
+ # If any key is missed in the given hash KeyError is raised with the first missed key
252
+ #
253
+ # @since 0.1.0
254
+ #
255
+ # @param keys [::Array of (#hash, #eql?)]
256
+ # @param hash [#to_h]
257
+ #
258
+ # @return [::Array[Object]]
259
+ #
260
+ # @example Base
261
+ # hash = { name: "John", age: 20, country: "USA" }
262
+ #
263
+ # F::Hash.fetch_values([:name, :age, :country], hash) # => ["John", 20, "USA"]
264
+ # F::Hash.fetch_values([:address, :email], hash) # => raise KeyError, "key not found: :address"
265
+ def fetch_values(keys = F._, hash = F._)
266
+ curry_implementation(:fetch_values, keys, hash)
267
+ end
268
+
269
+ # Returns an array of values stored by given keys
270
+ # If a key is missed the default value is returned
271
+ #
272
+ # @since 0.1.0
273
+ #
274
+ # @param keys [::Array of (#hash, #eql?)]
275
+ # @param hash [#to_h]
276
+ #
277
+ # @return [::Array[Object]]
278
+ #
279
+ # @example Base
280
+ # hash = { name: "John", age: 20, country: "USA" }
281
+ #
282
+ # F::Hash.values_at([:name, :age, :country], hash) # => ["John", 20, "USA"]
283
+ # F::Hash.values_at([:address, :email, :country, :age], hash) # => [nil, nil, "USA", 20]
284
+ def values_at(keys = F._, hash = F._)
285
+ curry_implementation(:values_at, keys, hash)
286
+ end
287
+
288
+ # Returns an array of all stored values
289
+ #
290
+ # @since 0.1.0
291
+ #
292
+ # @param hash [#to_h]
293
+ #
294
+ # @return [::Array[Object]]
295
+ #
296
+ # @example Base
297
+ # F::Hash.values({}) # => []
298
+ # F::Hash.values({ age: 33 }) # => [33]
299
+ # F::Hash.values({ age: 33, name: "John"}) # => [33, "John"]
300
+ def values(hash = F._)
301
+ curry_implementation(:values, hash)
302
+ end
303
+
304
+ # Returns an array of all stored keys
305
+ #
306
+ # @since 0.1.0
307
+ #
308
+ # @param hash [#to_h]
309
+ #
310
+ # @return [::Array[Object]]
311
+ #
312
+ # @example Base
313
+ # F::Hash.keys({}) # => []
314
+ # F::Hash.keys({ age: 33 }) # => [:age]
315
+ # F::Hash.keys({ age: 33, name: "John"}) # => [:age, :name]
316
+ def keys(hash = F._)
317
+ curry_implementation(:keys, hash)
318
+ end
319
+
320
+ # Returns a new hash containing only pairs
321
+ # for which a given function returns true
322
+ #
323
+ # @since 0.1.0
324
+ #
325
+ # @param function [#call/2]
326
+ # @param hash [#to_h]
327
+ #
328
+ # @return [::Array[Object]]
329
+ #
330
+ # @example Base
331
+ # hash = { 'a' => 1, 'b' => 2, :c => 3, :d => 4 }
332
+ # F::Hash.select(->(key, _value) { key.is_a?(String) }, hash) # => { 'a' => 1, 'b' => 2 }
333
+ # F::Hash.select(->(_key, value) { value.odd? }, hash) # => { 'a' => 1, :c => 3 }
334
+ def select(function = F._, hash = F._)
335
+ curry_implementation(:select, function, hash)
336
+ end
337
+
338
+ # Returns a new hash excluding pairs
339
+ # for which a given function returns true
340
+ #
341
+ # @since 0.1.0
342
+ #
343
+ # @param function [#call/2]
344
+ # @param hash [#to_h]
345
+ #
346
+ # @return [::Array[Object]]
347
+ #
348
+ # @example Base
349
+ # hash = { 'a' => 1, 'b' => 2, :c => 3, :d => 4 }
350
+ # F::Hash.reject(->(key, _value) { key.is_a?(String) }, hash) # => { :c => 3, :d => 4 }
351
+ # F::Hash.reject(->(_key, value) { value.odd? }, hash) # => { 'b' => 2, :d =>4 }
352
+ def reject(function = F._, hash = F._)
353
+ curry_implementation(:reject, function, hash)
354
+ end
355
+
356
+ # Returns a value stored by a given chain of keys.
357
+ # If a value of any level key of the chain is not found nil is returned.
358
+ #
359
+ # @since 0.1.0
360
+ #
361
+ # @param keys [::Array of (#hash, #eql?)]
362
+ # @param hash [#to_h]
363
+ # @return [::Array[Object]]
364
+ #
365
+ # @example Base
366
+ # hash = { a: { b: { c: 3 } } }
367
+ # F::Hash.dig([:a], hash) # => { b: { c: 3 } }
368
+ # F::Hash.dig([:a, :b], hash) # => { c: 3 }
369
+ # F::Hash.dig([:a, :b, :c], hash) # => 3
370
+ # F::Hash.dig([:foo], hash) # => nil
371
+ # F::Hash.dig([:a, :foo], hash) # => nil
372
+ # F::Hash.dig([:a, :b, :foo], hash) # => nil
373
+ def dig(keys = F._, hash = F._)
374
+ curry_implementation(:dig, keys, hash)
375
+ end
376
+
377
+ # Returns a value stored by a given chain of keys.
378
+ # If a value of any level key of the chain is not found KeyError is raised
379
+ #
380
+ # @since 0.1.0
381
+ #
382
+ # @param keys [::Array of (#hash, #eql?)]
383
+ # @param hash [#to_h]
384
+ # @return [::Array[Object]]
385
+ #
386
+ # @example Base
387
+ # hash = { a: { b: { c: 3 } } }
388
+ # F::Hash.dig!([:a], hash) # => { b: { c: 3 } }
389
+ # F::Hash.dig!([:a, :b], hash) # => { c: 3 }
390
+ # F::Hash.dig!([:a, :b, :c], hash) # => 3
391
+ # F::Hash.dig!([:foo], hash) # => raise KeyError, "key not found: :foo"
392
+ # F::Hash.dig!([:a, :foo], hash) # => raise KeyError, "key not found: :foo"
393
+ # F::Hash.dig!([:a, :b, :foo], hash) # => raise KeyError, "key not found: :foo"
394
+ def dig!(keys = F._, hash = F._)
395
+ curry_implementation(:dig!, keys, hash)
396
+ end
397
+
398
+ # Returns a new hash without all key-valued pairs
399
+ #
400
+ # @since 0.1.0
401
+ #
402
+ # @param hash [#to_h]
403
+ #
404
+ # @example Base
405
+ # hash = { name: "John", age: 18, email: nil, country: nil }
406
+ # F::Hash.compact(hash) # => { name: "John", age: 18 }
407
+ def compact(keys = F._, hash = F._)
408
+ curry_implementation(:compact, keys, hash)
409
+ end
410
+
411
+ # Returns a new hash with updated keys calculated
412
+ # as a result returned from a given function
413
+ #
414
+ # @since 0.1.0
415
+ #
416
+ # @param function [#call/1]
417
+ # @param hash [#to_h]
418
+ #
419
+ # @example Base
420
+ # hash = { a: 1, b: 2, c: 3 }
421
+ # F::Hash.transform_keys(->(key) { key.to_s }, hash) #=> { 'a' => 1, 'b' => 2, 'c' => 3 }
422
+ def transform_keys(function = F._, hash = F._)
423
+ curry_implementation(:transform_keys, function, hash)
424
+ end
425
+
426
+ # Returns a new hash with updated values calculated
427
+ # as a result returned from a given function
428
+ #
429
+ # @since 0.1.0
430
+ #
431
+ # @param function [#call/1]
432
+ # @param hash [#to_h]
433
+ #
434
+ # @example Base
435
+ # hash = { a: 1, b: 2, c: 3 }
436
+ # F::Hash.transform_values(->(value) { value.to_s }, hash) #=> { a: '1', b: '2', c: '3' }
437
+ def transform_values(function = F._, hash = F._)
438
+ curry_implementation(:transform_values, function, hash)
439
+ end
440
+
441
+ private
442
+
443
+ def _get(key, hash)
444
+ _hash(hash)[key]
445
+ end
446
+
447
+ def _fetch(key, hash)
448
+ _hash(hash).fetch(key)
449
+ end
450
+
451
+ def _fetch_with(key, fallback, hash)
452
+ _hash(hash).fetch(key, &fallback)
453
+ end
454
+
455
+ def _put(key, value, hash)
456
+ _hash(hash).merge(key => value)
457
+ end
458
+
459
+ def _merge(first, second)
460
+ _hash(first).merge(_hash(second))
461
+ end
462
+
463
+ if RUBY_VERSION >= "2.5"
464
+ def _slice(keys, hash)
465
+ _hash(hash).slice(*keys)
466
+ end
467
+ else
468
+ def _slice(keys, hash)
469
+ hash = _hash(hash)
470
+ keys.each_with_object({}) do |key, new_hash|
471
+ new_hash[key] = hash[key] if hash.key?(key)
472
+ end
473
+ end
474
+ end
475
+
476
+ def _slice!(keys, hash)
477
+ hash = _hash(hash)
478
+ missed_keys = keys - hash.keys
479
+ raise KeyError, "keys not found: #{missed_keys.inspect}" if missed_keys.any?
480
+
481
+ _slice(keys, hash)
482
+ end
483
+
484
+ def _fetch_values(keys, hash)
485
+ _hash(hash).fetch_values(*keys)
486
+ end
487
+
488
+ def _values_at(keys, hash)
489
+ _hash(hash).values_at(*keys)
490
+ end
491
+
492
+ def _values(hash)
493
+ _hash(hash).values
494
+ end
495
+
496
+ def _keys(hash)
497
+ _hash(hash).keys
498
+ end
499
+
500
+ def _select(function, hash)
501
+ _hash(hash).select(&function)
502
+ end
503
+
504
+ def _reject(function, hash)
505
+ _hash(hash).reject(&function)
506
+ end
507
+
508
+ def _dig(keys, hash)
509
+ _hash(hash).dig(*keys)
510
+ end
511
+
512
+ def _dig!(keys, hash)
513
+ hash = _hash(hash)
514
+ keys.reduce(hash) { |current, key| current.fetch(key) }
515
+ end
516
+
517
+ if RUBY_VERSION >= "2.4"
518
+ def _compact(hash)
519
+ _hash(hash).compact
520
+ end
521
+ else
522
+ def _compact(hash)
523
+ _hash(hash).reject { |_key, value| value.nil? }
524
+ end
525
+ end
526
+
527
+ if RUBY_VERSION >= "2.5"
528
+ def _transform_keys(function, hash)
529
+ _hash(hash).transform_keys(&function)
530
+ end
531
+ else
532
+ def _transform_keys(function, hash)
533
+ hash = _hash(hash)
534
+ hash.each_with_object({}) do |(key, value), new_hash|
535
+ new_key = function.(key)
536
+ new_hash[new_key] = value
537
+ end
538
+ end
539
+ end
540
+
541
+ if RUBY_VERSION >= "2.4"
542
+ def _transform_values(function, hash)
543
+ _hash(hash).transform_values(&function)
544
+ end
545
+ else
546
+ def _transform_values(function, hash)
547
+ hash = _hash(hash)
548
+ hash.each_with_object({}) do |(key, value), new_hash|
549
+ new_value = function.(value)
550
+ new_hash[key] = new_value
551
+ end
552
+ end
553
+ end
554
+
555
+ def _hash(hash)
556
+ hash.to_h
557
+ end
558
+ end
559
+ end
@@ -0,0 +1,72 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "common/helpers"
4
+
5
+ module FunRuby
6
+ # Module containing methods for strings
7
+ module String
8
+ include Common::Helpers
9
+
10
+ extend self
11
+
12
+ # Split a string by a passed delimiter
13
+ #
14
+ # @since 0.1.0
15
+ #
16
+ # @param splitter [::Array|::Regexp]
17
+ # @param string [::String]
18
+ #
19
+ # @return [::Array[::String]]
20
+ #
21
+ # @example Basics
22
+ # F::String.split("+", "1+2+3") #=> ["1", "2", "3"]
23
+ #
24
+ # @example Curried
25
+ # curried = F::String.split
26
+ # curried.("+").("1+2+3") #=> ["1", "2", "3"]
27
+ #
28
+ # @example Curried with placeholder
29
+ # curried = F::String.split(F._, "1+2+3")
30
+ # curried.("+") #=> ["1", "2", "3"]
31
+ def split(splitter = F._, string = F._)
32
+ curry_implementation(:split, splitter, string)
33
+ end
34
+
35
+ # Concats a string by a passed delimiter
36
+ #
37
+ # @since 0.1.0
38
+ #
39
+ # @param first [::String]
40
+ # @param second [::String]
41
+ #
42
+ # @return [::String]
43
+ #
44
+ # @example Basics
45
+ # F::String.concat("123", "456") #=> "123456"
46
+ #
47
+ # @example Curried
48
+ # curried = F::String.concat
49
+ # curried.("123").("456") #=> "123456"
50
+ #
51
+ # @example Curried with placeholder
52
+ # curried = F::String.concat(F._, "456")
53
+ # curried.("123") #=> "123456"
54
+ def concat(first = F._, second = F._)
55
+ curry_implementation(:concat, first, second)
56
+ end
57
+
58
+ private
59
+
60
+ def _split(splitter, string)
61
+ _string(string).split(splitter)
62
+ end
63
+
64
+ def _concat(first, second)
65
+ _string(first) + _string(second)
66
+ end
67
+
68
+ def _string(string)
69
+ string.to_s
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FunRuby
4
+ VERSION = "0.0.1"
5
+ end