Skip to content

Commit

Permalink
Merge pull request #25 from langleyfoxall/feature/random-weighted-value
Browse files Browse the repository at this point in the history
Add select random model from collection by weighted Column
  • Loading branch information
dextermb authored Sep 11, 2018
2 parents 54223f0 + 95c740d commit 88b2ac6
Show file tree
Hide file tree
Showing 2 changed files with 58 additions and 0 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ All methods can be called statically.
- [`getColumns`](#getcolumns)
- [`getNextId`](#getnextid)
- [`areRelated`](#arerelated)
- [`randomByWeightedValue`](#randombyweightedvalue)


##### `all`
Expand Down Expand Up @@ -127,6 +128,29 @@ $related = Models::areRelated($user, $post, [$comment, 'comments'])
| Throws| [Exception](http://php.net/manual/en/class.exception.php) or [InvalidArgumentException](http://php.net/manual/en/class.invalidargumentexception.php) |
| Returns | [Boolean](http://php.net/manual/en/language.types.boolean.php) |

##### `randomByWeightedValue`
Takes a collection of `Model`'s and returns one based upon a weighted column. It can also take a maxCap to simulate higher odds.

It should be noted when passing a `maxCap` you should pass in a desired return value if none of the items in the models list were hit.

###### Example Usage
```
$prizes = Prizes::all();
$selectedPrize = Models::randomByWeightedValue($models, 'chance');
```

```
//returns a prize as if the 'chance' column related to {$chance}/10,000,000 - if none are hit it will return null.
$selectedPrize = Models::randomByWeightedValue('App\Models\Prize', 'chance', 10000000, null);
```

| Key | Details |
| --- | ------- |
| Parameters | A [Collection](https://laravel.com/docs/collections) of [Models](https://laravel.com/docs/eloquent) or a string representation of a [Model](https://laravel.com/docs/eloquent), `column`, `maxCap` = null, `ifLose` = null |
| Throws| None |
| Returns | [Model](https://laravel.com/docs/eloquent) or an `object` |


---

### `IsRelatedTo`
Expand Down Expand Up @@ -154,6 +178,7 @@ $related = $user->isRelatedTo($post)
| Returns | [Boolean](http://php.net/manual/en/language.types.boolean.php) |
---


### `ApiResponse`
The [ApiResponse helper](src/LangleyFoxall/Helpers/ApiResponse.php) standardizes an API response. Always containing the same fields:

Expand Down
33 changes: 33 additions & 0 deletions src/LangleyFoxall/Helpers/Models.php
Original file line number Diff line number Diff line change
Expand Up @@ -176,4 +176,37 @@ public static function areRelated(...$relations)
return false;
}
}

/**
* @param Collection|String $models Either a collection of Model's or a String representation of a Model.
* @param String $column
* @param int $maxCap Enter this is the weights are odds (Eg: Weight = 1, $maxCap = 1000 for one in a thousand.)
* @param object $ifLose If you've entered a maxCap, set what gets returned if nothing gets hit.
* @return Model $model
*/
public static function randomByWeightedValue($models, $column, $maxCap = null, $ifLose = null) {
//If model string is passed in, get all model instances.
if (is_string($models)) {
$models = $models::whereNotNull($column)->get();
}

$indexToWeightArray = [];

foreach ($models as $index => $weightedBucket) {
$indexToWeightArray[$index] = $weightedBucket->$column;
}

$rand = mt_rand(1, $maxCap ?: (int)array_sum($indexToWeightArray));

$modelIndex = null;
foreach ($indexToWeightArray as $index => $value) {
$rand -= $value;
if ($rand <= 0) {
$modelIndex = $index;
break;
}
}

return $models[$modelIndex] ?? $ifLose;
}
}

0 comments on commit 88b2ac6

Please sign in to comment.