array_collection 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
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