🔃 recollect-array
Simple wrapper to filter array using Ruby and simple predicate conditions
## Motivation
Because in sometimes, we need filter array passing conditions. This gem simplify this work.
## Documentation
Version | Documentation
---------- | -------------
unreleased | https://github.com/thadeu/recollect-array/blob/main/README.md
## Table of Contents
- [Installation](#installation)
- [Configuration](#configuration)
- [Availables Predicates](#availables-predicates)
- [Usage](#usage)
- [Utilities](#utilities)
## Compatibility
| kind | branch | ruby |
| -------------- | ------- | ------------------ |
| unreleased | main | >= 2.5.8, <= 3.1.x |
## Installation
Use bundle
```ruby
bundle add recollect-array
```
or add this line to your application's Gemfile.
```ruby
gem 'recollect-array'
```
and then, require module
```ruby
require 'recollect/array'
```
## Configuration
Without configuration, because we use only Ruby. ❤️
## Availables Predicates for all values
| Type | Suffix | Value |
| ----------- | ----------- | ----------- |
| Equal | eq | Anywhere |
| NotEqual | noteq | Anywhere |
| Contains | cont | Anywhere |
| NotContains | notcont | Anywhere |
| Included | in | Anywhere |
| NotIncluded | notin | Anywhere |
| Start | start | Anywhere |
| NotStart | notstart | Anywhere |
| End | end | Anywhere |
| NotEnd | notend | Anywhere |
| LessThan | lt | Anywhere |
| LessThanEqual | lteq | Anywhere |
| GreaterThan | gt | Anywhere |
| GreaterThanEqual | gteq | Anywhere |
## Availables Predicates only when value is Hash
> 💡 Below predicates works only when value is Hash
| Type | Suffix | Value |
| ----------- | ----------- | ----------- |
| NotEqual | not_eq | Hash |
| NotContains | not_cont | Hash |
| NotIncluded | not_in | Hash |
| NotStart | not_start | Hash |
| NotEnd | not_end | Hash |
## Usage
Think that your data seems like this.
```ruby
data = [
{
id: 1,
name: 'Test #1',
email: 'test1@email1.com',
schedule: { all_day: true },
numbers: %w[1 2],
active: true,
count: 9
},
{
id: 2,
name: 'Test #2',
email: 'test2@email2.com',
schedule: { all_day: false },
numbers: %w[3 4],
active: true,
count: 10
},
{
id: 3,
name: 'Test #3',
email: 'test3@email3.com',
schedule: { all_day: false },
numbers: %w[5 6],
active: false,
count: 99
}
]
```
You can use one or multiples predicates in your filter. We see some use cases.
### Flexible Use Case (Hash)
**Equal**
```ruby
filters = {
active: {
eq: true
}
}
collection = Recollect::Array.filter(data, filters)
```
**NotEqual**
```ruby
filters = {
active: {
# noteq or not_eq
not_eq: true
}
}
collection = Recollect::Array.filter(data, filters)
```
**Nested Hash Paths**
```ruby
filters = {
'schedule.all_day': {
eq: true
}
}
collection = Recollect::Array.filter(data, filters)
```
**Nested Array Paths**
> Note the `.0` 🎉
```ruby
filters = {
'numbers.0': {
eq: '3'
}
}
collection = Recollect::Array.filter(data, filters)
```
```ruby
filters = {
numbers: {
in: '3' # or in: ['3']
}
}
collection = Recollect::Array.filter(data, filters)
```
Using default Equal predicate.
```ruby
Recollect::Array.filter(data, numbers: 3)
Recollect::Array.filter(data, active: true)
Recollect::Array.filter(data, id: 3)
```
If array, you can navigate into self, using `property.NUMBER.property`
```ruby
data = [
{
schedules: [
{
opened: true,
all_day: true
},
{
opened: false,
all_day: true
}
]
},
{
schedules: [
{
opened: false,
all_day: true
},
{
opened: false,
all_day: true
}
]
}
]
filters = {
'schedules.0.opened': {
eq: true
}
}
# OR
filters = {
'schedules[0]opened': {
eq: true
}
}
# OR
filters = {
'schedules.[0].opened': {
eq: true
}
}
collection = Recollect::Array.filter(data, filters)
# [{ schedules: [{ opened: true, all_day: true }, { opened: false, all_day: true }] }]
```
Amazing, you can pass a Callable value as value, like this.
```ruby
filters = {
'schedules.[0].opened': {
eq: -> { true }
}
}
# OR a Module
module ActiveTruthy
def self.call = true
end
module NumbersAvailable
def self.call = %w[1 2]
end
filters = {
'schedules.[0].opened': {
eq: ActiveTruthy
},
numbers: {
in: NumbersAvailable
}
}
# OR a Class
class ActiveFalsey
def self.call = false
end
filters = {
'schedules.[0].opened': {
eq: ActiveFalsey
}
}
collection = Recollect::Array.filter(data, filters)
```
**Combine conditions**
Yes, you can combine one or multiple predicates to filter you array.
```ruby
filters = {
active: { eq: true },
numbers: {
in: %w[5],
not_in: '10'
},
email: {
cont: 'email1',
not_cont: '@gmail'
},
'schedule.all_day': {
in: [true, false]
}
}
collection = Recollect::Array.filter(data, filters)
```
### Querystring Use Case
**Equal**
```ruby
filters = { active_eq: true }
collection = Recollect::Array.filter(data, filters)
```
**NotEqual**
```ruby
filters = { active_noteq: true }
collection = Recollect::Array.filter(data, filters)
```
**Nested Hash Paths**
```ruby
filters = { 'schedule.all_day_eq': false }
collection = Recollect::Array.filter(data, filters)
```
**Nested Array Paths**
> Note the `.0` 🎉
```ruby
filters = { 'numbers.0_eq': '3' }
collection = Recollect::Array.filter(data, filters)
```
```ruby
filters = { numbers_in: ['1'] }
collection = Recollect::Array.filter(data, filters)
expect(collection.size).to eq(1)
```
**Combine conditions**
Yes, you can combine one or multiple predicates to filter you array.
```ruby
filters = {
active_noteq: true,
numbers_in: %w[5],
email_cont: 'test3',
'schedule.all_day_eq': false
}
collection = Recollect::Array.filter(data, filters)
```
**Receive querystring in your route**
Like Ransack, imagine that you receive an querystring and you want to filter your Array. So, you can to do something like this.
> ⚠️ But security is your responsability, ok? Let's go!
```ruby
# receive querystring in your route
querystring = "active_noteq=true&numbers_in=5&email_cont=test3&schedule.all_day_eq=false"
# parse querystring and transform to Hash
params = URI.decode_www_form(querystring).to_h
# filter your collection using params directly.
collection = Recollect::Array.filter(data, filters)
# Beautiful, right? 🎉
```
## Utilities
### Recollect::Hashie.get(hash, path)
```ruby
user = {
id: 1,
name: 'Test #1',
email: 'test1@email1.com',
schedule: { all_day: true },
numbers: %w[1 2],
active: true,
count: 9
}
result = Recollect::Hashie.get(user, 'id')
-> 1
result = Recollect::Hashie.get(user, 'schedule.all_day')
-> true
result = Recollect::Hashie.get(user, 'numbers')
-> ['1', '2']
result = Recollect::Hashie.get(user, 'numbers.0')
-> 1
result = Recollect::Hashie.get(user, 'numbers[0]')
-> 1
result = Recollect::Hashie.get(user, 'numbers[0][1]')
result = Recollect::Hashie.get(user, 'numbers.[0].[1]')
```
### Recollect::Array.pluck(array, path)
```ruby
users = [
{
id: 1,
name: 'Test #1',
email: 'test1@email1.com',
schedule: { all_day: true },
numbers: %w[1 2],
active: true,
count: 9
},
{
id: 2,
name: 'Test #1',
email: 'test1@email1.com',
schedule: { all_day: true },
numbers: %w[1 2],
active: true,
count: 9
}
]
result = Recollect::Array.pluck(users, 'id')
$ -> [1, 2]
result = Recollect::Array.pluck(users, 'schedule.all_day')
$ -> [true, true]
```
[⬆️ Back to Top](#table-of-contents-)
## Development
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run `bundle install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/thadeu/recollect-array. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/thadeu/recollect-array/blob/master/CODE_OF_CONDUCT.md).
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).