array_collection 0.3.0 → 0.3.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e1b8d9912e71cb9bf842e79331cb5019bc5df52a6f520434cc693d9a23fe3309
4
- data.tar.gz: 1a34a7d6fc2cae2fac86f4951f9c7f11df31e7c97982af29231ff541b413a2dd
3
+ metadata.gz: ef33c3e653a6e11b9d4c92b2524b71f772cdb1e49399ab6d9da1138af9d08612
4
+ data.tar.gz: 4c029f0f9e11c739b26737bb7db34573221c377e76958b872a3813f87dab7810
5
5
  SHA512:
6
- metadata.gz: 651ee3fe050356295b805b8aaeb2264b83fc169ec8cac23ae7a65db8a84f35263885bf869fe87a2b8825d0e8216501dbe5c2484c844528215028ac3573ca914a
7
- data.tar.gz: 7bde4d6ea23ad092d88d6617c5552c2a98c2bddfc1daf67d6c3f89ffe1fe8c0751b3cc78eac0b5e200225bdc90c2e17ddadf249b47fec1b478619e153611bb47
6
+ metadata.gz: d6b3624550f8f9e00ddffcf950b198fe4f7bfc4c544ae258d9d4c2da1eac0a3ecfe4f09b05cbeff050b047d25d9ab2449926473417ceb63bd4a79aa762ac0d6f
7
+ data.tar.gz: 89011dea94eb965e5cdcadb013b92a388b5f3ed5a3fcd459c2bfc28a24a5cfef3c1af34099d92b3fca20511b379e99789bb4ea40e2afd59a3baea6b556fac1d9
data/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [0.3.1](https://github.com/WailanTirajoh/ruby_collection/compare/v0.3.0...v0.3.1) (2024-03-31)
6
+
7
+
8
+ ### Bug Fixes
9
+
10
+ * **lint:** use described_class ([afba961](https://github.com/WailanTirajoh/ruby_collection/commit/afba961936538978109642db7c582a5746bbd5ea))
11
+
12
+
13
+ ### Features
14
+
15
+ * **contional:** #unless ([#12](https://github.com/WailanTirajoh/ruby_collection/issues/12)) ([5d141b3](https://github.com/WailanTirajoh/ruby_collection/commit/5d141b3aa05eef1d5649ae1cd3aae14215ec5e17))
16
+ * **join:** cross join ([#14](https://github.com/WailanTirajoh/ruby_collection/issues/14)) ([9657d6b](https://github.com/WailanTirajoh/ruby_collection/commit/9657d6bab25af6d00b329bc6d72bc0b560846b5a))
17
+ * **json:** allow string keys as input ([#10](https://github.com/WailanTirajoh/ruby_collection/issues/10)) ([4b47b1d](https://github.com/WailanTirajoh/ruby_collection/commit/4b47b1de2b5f3a7f0d35186321ecaa8adc4048bb))
18
+
19
+
20
+
5
21
  # [0.3.0](https://github.com/WailanTirajoh/ruby_collection/compare/v0.2.8...v0.3.0) (2024-03-29)
6
22
 
7
23
 
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArrayCollection
4
+ module ArrayComponents
5
+ module DataHandling # rubocop:disable Style/Documentation
6
+ def wrap(value)
7
+ return [] if value.nil?
8
+ return value if value.is_a?(Array)
9
+
10
+ [value]
11
+ end
12
+
13
+ def append(array, *elements)
14
+ array + elements
15
+ end
16
+
17
+ def prepend(array, *elements)
18
+ elements + array
19
+ end
20
+
21
+ def diff(array1, array2)
22
+ (array1 - array2) | (array2 - array1)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArrayCollection
4
+ module ArrayComponents
5
+ module Filtering # rubocop:disable Style/Documentation
6
+ def filter(array, &block)
7
+ array.select(&block)
8
+ end
9
+
10
+ def where(array, *args)
11
+ if args.size == 2
12
+ key, value = args
13
+ array.select { |item| item[key] == value }
14
+ elsif args.size == 3
15
+ key, operator, value = args
16
+ array.select { |item| ArrayCollection::CollectionFilter.apply_operator(operator, item[key], value) }
17
+ else
18
+ raise ArgumentError, "Invalid number of arguments"
19
+ end
20
+ end
21
+
22
+ def where_not_nil(array)
23
+ array.compact
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "array_collection/collection_filter"
4
+
5
+ module ArrayCollection
6
+ module ArrayComponents
7
+ # Module containing methods for joining arrays
8
+ module Joining
9
+ def join(array1, array2, key1, key2)
10
+ inner_join(array1, array2, key1, key2)
11
+ end
12
+
13
+ def inner_join(array1, array2, key1, key2)
14
+ array1.reject do |item1|
15
+ matching_items = array2.select { |item2| item1[key1] == item2[key2] }
16
+ matching_items.each { |matched_item| item1.merge!(matched_item) }
17
+ matching_items.empty?
18
+ end
19
+ end
20
+
21
+ def left_join(array1, array2, key1, key2)
22
+ return array1 if array2.count.zero?
23
+
24
+ right_index = build_index(array2, key2)
25
+
26
+ result = []
27
+ array1.each do |left_item|
28
+ right_items = right_index[left_item[key1]] || [nil_attributes(array2)]
29
+ merge_items(result, left_item, right_items)
30
+ end
31
+ result
32
+ end
33
+
34
+ def right_join(array1, array2, key1, key2)
35
+ left_index = build_index(array1, key1)
36
+
37
+ result = []
38
+ array2.each do |right_item|
39
+ left_items = left_index[right_item[key2]] || [nil_attributes(array1)]
40
+ merge_items(result, right_item, left_items)
41
+ end
42
+ result
43
+ end
44
+
45
+ def full_join(array1, array2, key1, key2)
46
+ left_join_result = left_join(array1, array2, key1, key2)
47
+ right_join_result = right_join(array1, array2, key1, key2)
48
+
49
+ left_join_result.concat(right_join_result).uniq
50
+ end
51
+
52
+ def full_outter_join(array1, array2, key1, key2)
53
+ full_join(array1, array2, key1, key2)
54
+ end
55
+
56
+ def cross_join(array1, array2)
57
+ return cross_join_hashes(array1, array2) if array1.all?(Hash) && array2.all?(Hash)
58
+
59
+ array1.product(array2)
60
+ end
61
+
62
+ private
63
+
64
+ def cross_join_hashes(array1, array2)
65
+ array1.flat_map { |hash1| array2.map { |hash2| hash1.merge(hash2) } }
66
+ end
67
+
68
+ def build_index(array, key)
69
+ array.group_by { |item| item[key] }
70
+ end
71
+
72
+ def merge_items(result, left_item, right_items)
73
+ right_items.each do |right_item|
74
+ result << left_item.merge(right_item)
75
+ end
76
+ end
77
+
78
+ def nil_attributes(array)
79
+ array.first.keys.each_with_object({}) { |key, hash| hash[key] = nil }
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArrayCollection
4
+ module ArrayComponents
5
+ module KeyFiltering # rubocop:disable Style/Documentation
6
+ def key_by(records, key)
7
+ records.to_h { |record| [record[key].to_s, yield(record)] }
8
+ end
9
+
10
+ def except(array, *keys)
11
+ raise ArgumentError, "Empty key list" if keys.empty?
12
+
13
+ array.map { |hash| hash.except(*keys) }
14
+ end
15
+
16
+ def only(array, *keys)
17
+ raise ArgumentError, "Empty key list" if keys.empty?
18
+
19
+ array.map { |hash| hash.select { |k, _| keys.include?(k) } }
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArrayCollection
4
+ module ArrayComponents
5
+ module Mapping # rubocop:disable Style/Documentation
6
+ def map(array, &block)
7
+ array.map(&block)
8
+ end
9
+
10
+ def map_with_keys(hash, &block)
11
+ hash.transform_values(&block)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -2,112 +2,120 @@
2
2
 
3
3
  require_relative "collection_array"
4
4
  require_relative "hooks"
5
+ require_relative "json_parser"
5
6
 
6
7
  module ArrayCollection
7
8
  # Set of chainable collections
8
- class Collect
9
+ class Collect # rubocop:disable Metrics/ClassLength
9
10
  extend ArrayCollection::Hooks
10
11
 
11
12
  def initialize(items)
12
- @items = get_arrayable_items(items)
13
+ parsed_hash = parse(items)
14
+ @items, @is_json = get_arrayable_items(parsed_hash)
13
15
  end
14
16
 
15
17
  def all
16
- @items
18
+ parse_output(@items)
17
19
  end
18
20
 
19
21
  def count(&block)
20
22
  @items.count(&block)
21
23
  end
22
24
 
23
- def uniq
24
- self.class.new(@items.uniq)
25
+ def index_of(value)
26
+ @items.index(value)
25
27
  end
26
28
 
27
- def filter(&block)
28
- self.class.new(ArrayCollection::CollectionArray.filter(@items, &block))
29
+ def key_by(key, &block)
30
+ ArrayCollection::CollectionArray.key_by(@items, key.to_sym, &block)
29
31
  end
30
32
 
31
- def where(key, *args)
32
- self.class.new(ArrayCollection::CollectionArray.where(@items, key, *args))
33
+ def when(boolean)
34
+ return self if boolean == false
35
+
36
+ yield(self)
33
37
  end
34
38
 
35
- def where_not_nil
36
- self.class.new(ArrayCollection::CollectionArray.where_not_nil(@items))
39
+ def unless(boolean)
40
+ return self if boolean == true
41
+
42
+ yield(self)
37
43
  end
38
44
 
39
- def index_of(value)
40
- @items.index(value)
45
+ def append(value)
46
+ clone(ArrayCollection::CollectionArray.append(@items, value))
41
47
  end
42
48
 
43
- def key_by(key, &block)
44
- ArrayCollection::CollectionArray.key_by(@items, key, &block)
49
+ def prepend(value)
50
+ clone(ArrayCollection::CollectionArray.prepend(@items, value))
45
51
  end
46
52
 
47
- def sort(&block)
48
- self.class.new(@items.sort(&block))
53
+ def diff(items)
54
+ clone(ArrayCollection::CollectionArray.diff(@items, items))
49
55
  end
50
56
 
51
- def sort_desc(&block)
52
- self.class.new(@items.sort(&block).reverse)
57
+ def filter(&block)
58
+ clone(ArrayCollection::CollectionArray.filter(@items, &block))
53
59
  end
54
60
 
55
- def sort_by_key(key)
56
- self.class.new(@items.sort_by { |item| item[key] })
61
+ def where(key, *args)
62
+ clone(ArrayCollection::CollectionArray.where(@items, key, *args))
57
63
  end
58
64
 
59
- def append(value)
60
- self.class.new(ArrayCollection::CollectionArray.append(@items, value))
65
+ def where_not_nil
66
+ clone(ArrayCollection::CollectionArray.where_not_nil(@items))
61
67
  end
62
68
 
63
- def prepend(value)
64
- self.class.new(ArrayCollection::CollectionArray.prepend(@items, value))
69
+ def uniq
70
+ clone(@items.uniq)
65
71
  end
66
72
 
67
- def map(&block)
68
- self.class.new(ArrayCollection::CollectionArray.map(@items, &block))
73
+ def only(*keys)
74
+ clone(ArrayCollection::CollectionArray.only(@items, *keys.map(&:to_sym)))
69
75
  end
70
76
 
71
- def when(boolean)
72
- return self if boolean == false
77
+ def except(*keys)
78
+ clone(ArrayCollection::CollectionArray.except(@items, *keys.map(&:to_sym)))
79
+ end
73
80
 
74
- yield(self)
81
+ def map(&block)
82
+ clone(ArrayCollection::CollectionArray.map(@items, &block))
75
83
  end
76
84
 
77
- def only(*keys)
78
- self.class.new(ArrayCollection::CollectionArray.only(@items, *keys))
85
+ def sort(&block)
86
+ clone(@items.sort(&block))
79
87
  end
80
88
 
81
- def except(*keys)
82
- self.class.new(ArrayCollection::CollectionArray.except(@items, *keys))
89
+ def sort_desc(&block)
90
+ clone(@items.sort(&block).reverse)
83
91
  end
84
92
 
85
- def diff(items)
86
- self.class.new(ArrayCollection::CollectionArray.diff(@items, items))
93
+ def sort_by_key(key)
94
+ clone(@items.sort_by { |item| item[key.to_sym] })
87
95
  end
88
96
 
89
97
  def inner_join(items, left_key, right_key)
90
- self.class.new(ArrayCollection::CollectionArray.inner_join(@items, get_arrayable_items(items), left_key,
91
- right_key))
98
+ clone(ArrayCollection::CollectionArray.inner_join(@items, get_arrayable_items(items), left_key, right_key))
92
99
  end
93
100
 
94
101
  def left_join(items, left_key, right_key)
95
- self.class.new(ArrayCollection::CollectionArray.left_join(@items, get_arrayable_items(items), left_key,
96
- right_key))
102
+ clone(ArrayCollection::CollectionArray.left_join(@items, get_arrayable_items(items), left_key, right_key))
97
103
  end
98
104
 
99
105
  def right_join(items, left_key, right_key)
100
- self.class.new(ArrayCollection::CollectionArray.right_join(@items, get_arrayable_items(items), left_key,
101
- right_key))
106
+ clone(ArrayCollection::CollectionArray.right_join(@items, get_arrayable_items(items), left_key, right_key))
102
107
  end
103
108
 
104
109
  def full_join(items, left_key, right_key)
105
- self.class.new(ArrayCollection::CollectionArray.full_join(@items, get_arrayable_items(items), left_key,
106
- right_key))
110
+ clone(ArrayCollection::CollectionArray.full_join(@items, get_arrayable_items(items), left_key, right_key))
107
111
  end
108
112
 
109
113
  private
110
114
 
115
+ def clone(result)
116
+ self.class.new(parse_output(result))
117
+ end
118
+
111
119
  def get_arrayable_items(items)
112
120
  case items
113
121
  when Array
@@ -123,6 +131,25 @@ module ArrayCollection
123
131
  raise ArgumentError, "Input must be an array of hashes" unless @items.all?(Hash)
124
132
  end
125
133
 
134
+ def json?
135
+ @is_json
136
+ end
137
+
138
+ def parse(items)
139
+ is_json = ArrayCollection::JsonParser.json_like?(items)
140
+ result = is_json ? ArrayCollection::JsonParser.parse_to_hash(items) : items
141
+
142
+ [get_arrayable_items(result), is_json]
143
+ end
144
+
145
+ def parse_output(items)
146
+ if json?
147
+ ArrayCollection::JsonParser.parse_to_json(items)
148
+ else
149
+ items
150
+ end
151
+ end
152
+
126
153
  # Hooks
127
154
  before :key_by, :check_hash_item
128
155
  before :only, :check_hash_item
@@ -1,136 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "collection_filter"
3
+ require_relative "array_components/filtering"
4
+ require_relative "array_components/mapping"
5
+ require_relative "array_components/data_handling"
6
+ require_relative "array_components/key_filtering"
7
+ require_relative "array_components/joining"
4
8
 
5
9
  module ArrayCollection
6
10
  # Sets of array utilities methods
7
11
  class CollectionArray
8
12
  class << self
9
- def filter(array, &block)
10
- array.select(&block)
11
- end
12
-
13
- def where(array, *args)
14
- if args.size == 2
15
- key, value = args
16
- array.select { |item| item[key] == value }
17
- elsif args.size == 3
18
- key, operator, value = args
19
- array.select { |item| ArrayCollection::CollectionFilter.apply_operator(operator, item[key], value) }
20
- else
21
- raise ArgumentError, "Invalid number of arguments"
22
- end
23
- end
24
-
25
- def where_not_nil(array)
26
- array.compact
27
- end
28
-
29
- def wrap(value)
30
- return [] if value.nil?
31
- return value if value.is_a?(Array)
32
-
33
- [value]
34
- end
35
-
36
- def append(array, *elements)
37
- array + elements
38
- end
39
-
40
- def prepend(array, *elements)
41
- elements + array
42
- end
43
-
44
- def map(array, &block)
45
- array.map(&block)
46
- end
47
-
48
- def map_with_keys(hash, &block)
49
- hash.transform_values(&block)
50
- end
51
-
52
- def key_by(records, key)
53
- records.to_h { |record| [record[key].to_s, yield(record)] }
54
- end
55
-
56
- def except(array, *keys)
57
- raise ArgumentError, "Empty key list" if keys.empty?
58
-
59
- array.map { |hash| hash.except(*keys) }
60
- end
61
-
62
- def only(array, *keys)
63
- raise ArgumentError, "Empty key list" if keys.empty?
64
-
65
- array.map { |hash| hash.select { |k, _| keys.include?(k) } }
66
- end
67
-
68
- def diff(array1, array2)
69
- (array1 - array2) | (array2 - array1)
70
- end
71
-
72
- def join(array1, array2, key1, key2)
73
- inner_join(array1, array2, key1, key2)
74
- end
75
-
76
- def inner_join(array1, array2, key1, key2)
77
- array1.reject do |item1|
78
- matching_items = array2.select { |item2| item1[key1] == item2[key2] }
79
- matching_items.each { |matched_item| item1.merge!(matched_item) }
80
- matching_items.empty?
81
- end
82
- end
83
-
84
- def left_join(array1, array2, key1, key2)
85
- return array1 if array2.count.zero?
86
-
87
- right_index = build_index(array2, key2)
88
-
89
- result = []
90
- array1.each do |left_item|
91
- right_items = right_index[left_item[key1]] || [nil_attributes(array2)]
92
- merge_items(result, left_item, right_items)
93
- end
94
- result
95
- end
96
-
97
- def right_join(array1, array2, key1, key2)
98
- left_index = build_index(array1, key1)
99
-
100
- result = []
101
- array2.each do |right_item|
102
- left_items = left_index[right_item[key2]] || [nil_attributes(array1)]
103
- merge_items(result, right_item, left_items)
104
- end
105
- result
106
- end
107
-
108
- def full_join(array1, array2, key1, key2)
109
- left_join_result = left_join(array1, array2, key1, key2)
110
- right_join_result = right_join(array1, array2, key1, key2)
111
-
112
- left_join_result.concat(right_join_result).uniq
113
- end
114
-
115
- def full_outter_join(array1, array2, key1, key2)
116
- full_join(array1, array2, key1, key2)
117
- end
118
-
119
- private
120
-
121
- def build_index(array, key)
122
- array.group_by { |item| item[key] }
123
- end
124
-
125
- def merge_items(result, left_item, right_items)
126
- right_items.each do |right_item|
127
- result << left_item.merge(right_item)
128
- end
129
- end
130
-
131
- def nil_attributes(array)
132
- array.first.keys.each_with_object({}) { |key, hash| hash[key] = nil }
133
- end
13
+ include ArrayCollection::ArrayComponents::Filtering
14
+ include ArrayCollection::ArrayComponents::Mapping
15
+ include ArrayCollection::ArrayComponents::DataHandling
16
+ include ArrayCollection::ArrayComponents::KeyFiltering
17
+ include ArrayCollection::ArrayComponents::Joining
134
18
  end
135
19
  end
136
20
  end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ArrayCollection
4
+ # JSON Parser for collection
5
+ class JsonParser
6
+ class << self
7
+ def json_like?(input)
8
+ return false if input.nil? || input.count.zero?
9
+
10
+ hash = input[0]
11
+ hash.is_a?(Hash) && hash.keys.all?(String)
12
+ end
13
+
14
+ def parse_to_hash(json_like)
15
+ json_like.map { |hash| deep_string_to_symbol(hash) }
16
+ end
17
+
18
+ def parse_to_json(hashes)
19
+ hashes.map { |hash| deep_stringify_keys(hash) }
20
+ end
21
+
22
+ private
23
+
24
+ def deep_string_to_symbol(hash)
25
+ return hash unless hash.is_a?(Hash)
26
+
27
+ hash.each_with_object({}) do |(key, value), result|
28
+ new_key = key.is_a?(String) ? key.to_sym : key
29
+ result[new_key] = deep_string_to_symbol(value)
30
+ end
31
+ end
32
+
33
+ def deep_stringify_keys(hash)
34
+ hash.each_with_object({}) do |(key, value), result|
35
+ new_key = key.to_s
36
+ new_value = value.is_a?(Hash) ? deep_stringify_keys(value) : value
37
+ result[new_key] = new_value
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ArrayCollection
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: array_collection
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wailan Tirajoh
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-03-29 00:00:00.000000000 Z
11
+ date: 2024-03-31 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Write a longer description or delete this line.
14
14
  email:
@@ -28,12 +28,18 @@ files:
28
28
  - Rakefile
29
29
  - collection.gemspec
30
30
  - lib/array_collection.rb
31
+ - lib/array_collection/array_components/data_handling.rb
32
+ - lib/array_collection/array_components/filtering.rb
33
+ - lib/array_collection/array_components/joining.rb
34
+ - lib/array_collection/array_components/key_filtering.rb
35
+ - lib/array_collection/array_components/mapping.rb
31
36
  - lib/array_collection/collect.rb
32
37
  - lib/array_collection/collection_array.rb
33
38
  - lib/array_collection/collection_filter.rb
34
39
  - lib/array_collection/data_accessor.rb
35
40
  - lib/array_collection/helper.rb
36
41
  - lib/array_collection/hooks.rb
42
+ - lib/array_collection/json_parser.rb
37
43
  - lib/array_collection/version.rb
38
44
  - package.json
39
45
  - sig/collection.rbs