I have an array with recipes from 3 categories, breakfast, lunch and dinner. Each of these categories, have 10 unique recipes.
$recipes = [
'breakfast' => [
0 => [
'title' => 'eggless waffles',
'calorie' => 210,
],
1 => [
'title' => 'blueberry oatmeal',
'calorie' => 161,
],
...
],
'lunch' => [9],
'dinner' => [9]
];
I'd like to sort and create a combination of 3 recipes for each day
$days = array_fill(0, 6, [1 => [], 2 => [], 3 => []]);
Each recipe has a calorie amount, and each final day should have a combination (consists of 1 breakfast, 1 lunch and 1 dinner) with recipes that was ordered by whichever 3 recipe combo hit closest to 500
For example, if day 1 combined recipes (breakfast, lunch and dinner) calorie totaled 660, and day 2 was 400. It's possible that switching breakfast from day 2, to day 1 might make both of them hit closest to 500, however it's possible that switching day 3 breakfast to day 1, and day 2 to day 3 might make all 3 hit closer to 500 as well.
So day 1, 2, 3, 4, 5, 6, and 7 should have 3 recipes (breakfast, lunch and dinner)
$final = [
0 => [
'breakfast' => [...],
'lunch' => [...],
'dinner' => [...],
],
1 => [
'breakfast' => [...],
'lunch' => [...],
'dinner' => [...],
],
2 => [
'breakfast' => [...],
'lunch' => [...],
'dinner' => [...],
],
...
];
It's been days since I've reached an impasse, and I cannot figure out how to go about sorting these arrays into a combination of 3 for each day. (I know I'm not providing a lot of code to go off of)
Edit 1:
This is what I've got so far:
class Combinations {
private $days;
public function __construct(){
$this->days = array_fill(1, 7, [1 => [], 2 => [], 3 => []]);
}
public function create(){
$median = 600;
foreach($this->days as $day => $categories){
while($this->dayIsIncomplete($day)){
$recipes = [];
foreach($categories as $category => $value){
$recipes[$category] = $this->getRandomRecipe($category);
}
// add random meals to first day
if($day === 1){
$this->days[$day] = $recipes;
continue;
}
foreach($recipes as $category => $recipe){
foreach($this->days as $dayKey => $mealsArray){
$originalMacros = $this->totalMacros($mealsArray);
// remove $recipe category from mealsArray, and merge it ($recipe)
$filteredMacros = $this->totalMacros(array_merge([$recipe], array_filter($mealsArray, function($key) use($category){
return $key !== $category;
}, ARRAY_FILTER_USE_KEY)));
// if original is not closer to median
if(($originalMacros - $median) * ($originalMacros - $median) < ($filteredMacros - $median) * ($filteredMacros - $median)){
// flip current recipes
// switch D2B ($recipe) with D1B
}
}
}
}
}
}
public function getRandomRecipe(int $category){
$recipes = []
if($category === 1){
$recipes[] = ['id' => 1, 'calorie' => 310];
$recipes[] = ['id' => 2, 'calorie' => 360];
$recipes[] = ['id' => 3, 'calorie' => 450];
$recipes[] = ['id' => 4, 'calorie' => 330];
$recipes[] = ['id' => 5, 'calorie' => 220];
$recipes[] = ['id' => 6, 'calorie' => 390];
$recipes[] = ['id' => 7, 'calorie' => 400];
$recipes[] = ['id' => 8, 'calorie' => 320];
$recipes[] = ['id' => 9, 'calorie' => 460];
}
if($category === 2){
$recipes[] = ['id' => 10, 'calorie' => 420];
$recipes[] = ['id' => 11, 'calorie' => 360];
$recipes[] = ['id' => 12, 'calorie' => 450];
$recipes[] = ['id' => 13, 'calorie' => 310];
$recipes[] = ['id' => 14, 'calorie' => 320];
$recipes[] = ['id' => 15, 'calorie' => 490];
$recipes[] = ['id' => 16, 'calorie' => 440];
$recipes[] = ['id' => 17, 'calorie' => 520];
$recipes[] = ['id' => 18, 'calorie' => 560];
}
if($category === 3){
$recipes[] = ['id' => 19, 'calorie' => 510];
$recipes[] = ['id' => 20, 'calorie' => 660];
$recipes[] = ['id' => 21, 'calorie' => 750];
$recipes[] = ['id' => 22, 'calorie' => 610];
$recipes[] = ['id' => 23, 'calorie' => 580];
$recipes[] = ['id' => 24, 'calorie' => 690];
$recipes[] = ['id' => 25, 'calorie' => 710];
$recipes[] = ['id' => 26, 'calorie' => 620];
$recipes[] = ['id' => 27, 'calorie' => 730];
}
return $recipes[array_rand($recipes)];
}
public function dayIsIncomplete($day){
return !empty($this->days[$day][1]) && !empty($this->days[$day][2]) && !empty($this->days[$day][3]);
}
public function totalMacros($array){
$total = 0;
foreach ($array as $key => $value) {
$total += $value['calorie'];
}
return $total / 2;
}
}
Edit 2:
I'm trying to figure out what algorithm best fits to sort this issue out. I think using a bipartite matching (maximum) algorithm might be what I need.
Edit 3:
Thank you all for taking the time to help, I haven't forgotten about the answers. I had to put this aside for the time being, however soon enough I'll get to it, and the accepted answer will get my remaining 300 bounty.
So I tested a genetic algorithm and it works. I used Jenetics, a Java library (it's not PHP, sorry, but PHP is not suited to heavy computations anyway).
I took 1400 calories as the daily target.
The function to be minimized is the mean squared error.
Here's the code:
import java.util.ArrayList;
import io.jenetics.*;
import io.jenetics.engine.*;
import io.jenetics.util.*;
public class Recipes
{
private static final int TARGET = 1400;
private static final int DAYS = 7;
private static class Recipe
{
public int id;
public int calories;
public Recipe(int id, int calories)
{
this.id = id;
this.calories = calories;
}
}
private static ISeq<Recipe> getSeq(int[] ids, int[] calories)
{
ArrayList<Recipe> list = new ArrayList<>();
for(int i=0;i<ids.length;i++)
list.add(new Recipe(ids[i], calories[i]));
return ISeq.of(list);
}
private static double meanSquareError(Genotype<EnumGene<Recipe>> gt)
{
int err = 0;
for(int d=0;d<DAYS;d++)
{
int calories = 0;
for(int m=0;m<3;m++)
calories += gt.get(m).get(d).allele().calories;
err += (calories-TARGET)*(calories-TARGET);
}
return err / (double)DAYS;
}
public static void main(String[] args)
{
ISeq<Recipe> recipes1 = getSeq(new int[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9}, new int[]{310, 360, 450, 330, 220, 390, 400, 320, 460});
ISeq<Recipe> recipes2 = getSeq(new int[]{10, 11, 12, 13, 14, 15, 16, 17, 18}, new int[]{420, 360, 450, 310, 320, 490, 440, 520, 560});
ISeq<Recipe> recipes3 = getSeq(new int[]{19, 20, 21, 22, 23, 24, 25, 26, 27}, new int[]{510, 660, 750, 610, 580, 690, 710, 620, 730});
Factory<Genotype<EnumGene<Recipe>>> gtf = Genotype.of(
PermutationChromosome.of(recipes1, DAYS),
PermutationChromosome.of(recipes2, DAYS),
PermutationChromosome.of(recipes3, DAYS)
);
Engine<EnumGene<Recipe>, Double> engine = Engine
.builder(Recipes::meanSquareError, gtf)
.optimize(Optimize.MINIMUM)
.populationSize(50)
.alterers(new SwapMutator<>(0.2), new PartiallyMatchedCrossover<>(0.2), new Mutator<>(0.01))
.build();
Phenotype<EnumGene<Recipe>, Double> result = engine.stream()
.limit(20000)
.collect(EvolutionResult.toBestPhenotype());
for(int m=0;m<3;m++)
{
for(int d=0;d<DAYS;d++)
{
Recipe r = result.genotype().get(m).get(d).allele();
System.out.print(String.format("%2d (%d) ", r.id, r.calories));
}
System.out.println();
}
System.out.println("MSE = " + result.fitness());
}
}
A genetic algorithm is non-deterministic so it gives a different result each time. The best solution I could get is:
3 (450) 4 (330) 5 (220) 2 (360) 7 (400) 1 (310) 8 (320)
16 (440) 15 (490) 17 (520) 10 (420) 13 (310) 11 (360) 14 (320)
19 (510) 23 (580) 20 (660) 26 (620) 24 (690) 27 (730) 21 (750)
MSE = 14.285714
It's almost perfect (all days are at 1400 calories except Sunday which has 1390).
You have:
10 breakfasts
10 lunches
10 dinners
The combinations of these are 10x10x10 = 1000 recipes.
Part 1
Calculate these recipes and their total calories each.
From each recipes `s total calories, calculate the absolute difference from the daily calories goal:
AbsoluteDifference = Abs(calories - 500)
and which breakfast, lunch and dinner it consists of.
So now e.g. you have a list:
| Recipes | AbsDiff | Breakfast | Lunch | Dinner |
| recipe 1 | 140 | 1 | 7 | 4
| recipe 2 | 135 | 4 | 8 | 3
| recipe 3 | 210 | 7 | 9 | 10
...
| recipe 1000 | 170 | 5 | 1 | 9
Part 2
This is the difficult part, by calculating all combinations of 7 recipes is gonna take too long.
The best combination is the one that has total of absDiff of its recipes the minimum:
MIN(AbsDiff(recipe1) + AbsDiff(recipe2) + AbsDiff(recipe7))
Algorithm
The idea is to calculate only a few combinations of 7 different recipes.
Initial Step
Make a guess of "how much the minimum could be about", e.g. say you think it is likely less than 350 calories.
Using this guess, you can try to calculate all the combinations of 7 recipes that have TotalCaloriesDiff < 350.
Based on:
In a collection of n positive numbers that sum up to or less than S, at least one of them will be less than S divided by n (S/n).
In this case S=350 and n=7, then at least one recipe should have AbsDiff < 350/7 = 50.
So, you could try to calculate combinations of 7 recipes with lower total differences to the guess.
Steps
Get the recipes with AbsDiff(recipe1) < 350 / 7
For each recipe1 found above, get the next recipes that have AbsDiff(recipe2) < (350 - AbsDiff(recipe1)) / 6 and they do not share any breakfast, lunch or dinner with recipe1.
continue till you get combinations of 7 recipes.
select the combination with lowest TotalCaloriesDiff
If you do not find any results, based on your guess, you raise the guess, e.g. 350 + 50 = 400.
Here is my answer to a similar problem.
I think first you should make a combination of dishes, where calorie should be near to 500, to make them unice, like:
$arr = [
0 => [
'breakfast' => 160
'lunch' => 160
'dinner' => 180
],
...
You should rebuild array like $breakfast = ['1' => 130, '2' => 150, '3' => 160, '4' => 170, '5' => 120, '6' => 100, '7' => 130] and etc.
Maybe try to compare arrays like
$final = ['1' => 152, '2' => 235, '3' => 521, '4' => 343, ... ];
And then you can grab each value from $arr ->
$final = ['1' => ['breakfast' => '1', 'lunch' => '5', 'dinner' => '2'], ...];
I think you can modify this logic as you want. Best of luck
Related
I have a php array below and i want to know how to get number of companies who did a training course. Look below:
Array
(
[0] => Array
(
[date_creation] => Apr 10, 2021 10:17 pm
[idformation] => 84
[idsociete] => 7
[training] => ELECTRICAL SAFETY TRAINING
[company] => ALUCAM
)
[1] => Array
(
[date_creation] => Apr 10, 2021 10:55 pm
[idformation] => 84
[idsociete] => 7
[training] => ELECTRICAL SAFETY TRAINING
[company] => ALUCAM
)
[2] => Array
(
[date_creation] => Apr 12, 2021 03:27 pm
[idformation] => 104
[idsociete] => 201
[training] => FORKLIFT, JLG SCISSOR LIFT, AERAL PLATFORM
[company] => US EMBASSY
)
);
Each array represents the record of a worker in the database from a company say Alucam and did training Electrical safety.
So from the array above i want to get something like:
2 Alucams did electrical safety as seen in the array.
I just need a clue on how to get the count of persons who did a particular training from the array.
Please help
I assume you can have the same training from different companies, opposite case you can simplified the code.
Input data (I simplified your input array, including only the fields I need):
$workers = array(array("training" => "ELECTRICAL SAFETY TRAINING", "company" => "ALUCAM"),
array("training" => "ELECTRICAL SAFETY TRAINING", "company" => "ALUCAM"),
array("training" => "FORKLIFT, JLG SCISSOR LIFT, AERAL PLATFORM", "company" => "US EMBASSY"),
array("training" => "FORKLIFT, JLG SCISSOR LIFT, AERAL PLATFORM", "company" => "ALUCAM")
);
Php code:
$trainingCount = array();
foreach($workers as $worker) {
$training = $worker["training"];
$company = $worker["company"];
if(! array_key_exists($training, $trainingCount)) {
$trainingCount[$training] = array();
}
if(! array_key_exists($company, $trainingCount[$training])) {
$trainingCount[$training][$company] = 0;
}
$trainingCount[$training][$company]++;
}
Result:
array('ELECTRICAL SAFETY TRAINING' => array('ALUCAM' => 2), 'FORKLIFT, JLG SCISSOR LIFT, AERAL PLATFORM' => array('US EMBASSY' => 1, 'ALUCAM' => 1));
Effectively you have a list of employees with their training listed in a comma separated list?
So basically you need to iterate through the list stripping out the information you require (company & training). Then every time you get a match you increment the matching data.
There are a few ways to do this the simplest would be to iterate through the results to create an array which looks something like...
$countArray = [
"Alucam" => [
"ELECTRICAL SAFETY TRAINING" = 2,
],
];
The code would look like:
$countArray = [];
// Generate the array
foreach ($array as $employee) {
$trainingList = array_map("trim", explode(",", $employee["training"]));
foreach ($trainingList as $training) {
$countArray[$employee["company"]][$training] = ($countArray[$employee["company"]][$training] ?? 0) + 1;
}
}
// Generate the output
foreach ($countArray as $companyName => $training) {
foreach ($training as $trainingName => $trainingCount) {
echo "{$trainingCount} {$companyName} did {$trainingName}", PHP_EOL;
}
}
/*
Output:
2 ALUCAM did ELECTRICAL SAFETY TRAINING
1 US EMBASSY did FORKLIFT
1 US EMBASSY did JLG SCISSOR LIFT
1 US EMBASSY did AERAL PLATFORM
*/
However, this does mean you can have "unusual" characters in array keys which could lead to problems further down the line. So you may do better with a slightly more complicated approach (i.e. having index arrays for the company and training names) which gives an array a little something like...
$countArray = [
'company' => [
0 => 'ALUCAM',
1 => 'US EMBASSY',
],
'training' => [
0 => 'ELECTRICAL SAFETY TRAINING',
1 => 'FORKLIFT',
2 => 'JLG SCISSOR LIFT',
3 => 'AERAL PLATFORM',
],
'count' => [
0 => [
0 => 2,
],
1 => [
1 => 1,
2 => 1,
3 => 1,
],
],
];
The code would look like:
// Generate the array
foreach ($array as $employee) {
if (false === ($companyIndex = array_search($employee["company"], $countArray["company"]))) {
$companyIndex = count($countArray["company"]);
$countArray["company"][] = $employee["company"];
}
$trainingList = array_map("trim", explode(",", $employee["training"]));
foreach ($trainingList as $training) {
if (false === ($trainingIndex = array_search($training, $countArray["training"]))) {
$trainingIndex = count($countArray["training"]);
$countArray["training"][] = $training;
}
$countArray["count"][$companyIndex][$trainingIndex] = ($countArray["count"][$companyIndex][$trainingIndex] ?? 0) + 1;
}
}
// Generate the output
foreach ($countArray["count"] as $companyKey => $companyCount) {
$companyName = $countArray["company"][$companyKey];
foreach ($companyCount as $trainingKey => $trainingCount) {
$trainingName = $countArray["training"][$trainingKey];
echo "{$trainingCount} {$companyName} did {$trainingName}", PHP_EOL;
}
}
You can use array_count_values and array_column to achieve something like this: You can modify as required.
$arr = [
['date_creation' => 'Apr 10, 2021 10:17 pm', 'idformation' => 84, 'idsociete' => 7, 'training' => 'ELECTRICAL SAFETY TRAINING', 'company' => 'ALUCAM'],
['date_creation' => 'Apr 10, 2021 10:17 pm', 'idformation' => 84, 'idsociete' => 7, 'training' => 'ELECTRICAL SAFETY TRAINING', 'company' => 'ALUCAM'],
['date_creation' => 'Apr 12, 2021 03:27 pm', 'idformation' => 104, 'idsociete' => 201, 'training' => 'FORKLIFT, JLG SCISSOR LIFT, AERAL PLATFORM', 'company' => 'US EMBASSY'],
];
$training = 'ALUCAM';
$companies = array_count_values(array_column($arr, 'company'))[$training]; // outputs: 2
Customers of a furniture store website can select products and add them to a "style book". Each product belongs to a "style".
The furniture store has some stylists that each have made their own style book that represents their style and expertise.
I want to be able to find the stylist that best matches a customer's stylebook.
For each style book I have a count of the number of products per style.
$stylists = [
'Nanda' => [
'Design' => 20,
'Retro' => 0,
'Rustiek' => 0,
],
'Angelique' => [
'Design' => 0,
'Retro' => 20,
'Rustiek' => 0,
],
'Lissy' => [
'Design' => 10,
'Retro' => 10,
'Rustiek' => 0,
],
];
The same for the customer's style book:
$customer = [
'Design' => 15,
'Retro' => 10,
'Rustiek' => 0,
];
In this case Lissy should be the best match.
The number of products isn't important since this depends on how active the stylist is.
More important is that the stylist matches most of the customer's styles.
For example:
'Stylist' => [
'Design' => 10,
'Retro' => 10,
'Rustiek' => 0,
]
Should still be a better match than
'Stylist' => [
'Design' => 300,
'Retro' => 0,
'Rustiek' => 180,
]
I have tried giving the stylists' style books scores and percentages based on the order of importance of the customer's style book but still I don't get the best match a 100% of the times.
Google also didn't get me anywhere.
As we have already discussed, the problem with your model is, that it relies on the number of products. But what we need is an indicator of the style the stylist is using. In other words we eliminate the count and replace it with a relatively weighted indicator (percentages in this case). For example a stylist with a product portfolio of:
[
style1 => 30,
style2 => 10,
style3 => 5
]
The product count is 45 = 30 + 10 + 5 this will result in a style-profile like this:
[
style1 => 0.66,
style2 => 0.22,
style3 => 0.11
]
To match the stylist-style-profile with the client-style-profile we need to do the same thing for the client-stylebook [15, 10, 0]:
[
style1 => 0.60
style2 => 0.40
style3 => 0.00
]
The idea behind this is, that we rate how a stylist is influenced by a certain style and the outcome will probably be quite similar for the product that we want to find the best fitting stylist to.
If the stylist made products in a style that is not really what we need for the match, we rate this fact with the weighted relative factor e.g. 0.11. It is not that important, but we still acknowledge the fact that the design might be somewhat biased.
Therefore, if a stylist has a lot of products with a certain style that we are not looking for, it won't change the outcome as much.
Please let me know, if this helps and if you want to change anything. From here we could also implement other options and rules.
Below you find my RatingModel.
<?php
class RatingModel {
private $name;
private $preferences;
private $preferencesWeighted;
public function RatingModel($name, array $preferences) {
$this->name = $name;
$this->preferences = $preferences;
$this->init();
}
private function init() {
$total = 0;
foreach ($this->preferences as $value) {
$total += $value;
}
if ($total > 0) {
foreach ($this->preferences as $value) {
$this->preferencesWeighted[] = $value / $total;
}
} else {
$this->preferencesWeighted = array_fill(0, sizeof($this->preferences), 0);
}
}
public function getName() {
return $this->name;
}
public function getPreferences() {
return $this->preferences;
}
public function getPreferencesWeighted() {
return $this->preferencesWeighted;
}
public function distanceToModel($ratingModel) {
$delta = [];
for ($i = 0; $i < sizeof($this->preferencesWeighted); $i++) {
$delta[] = abs($this->preferencesWeighted[$i] - $ratingModel->getPreferencesWeighted()[$i]);
}
return $delta;
}
public function scoreToModel($ratingModel) {
$distanceToModel = $this->distanceToModel($ratingModel);
$score = [];
foreach ($distanceToModel as $value) {
$score[] = $value * $value;
}
return sqrt(array_sum($score));
}
}
$customer = new RatingModel('Customer', [15, 10, 0]);
$nanda = new RatingModel('Nanda', [20, 0, 0]);
$angelique = new RatingModel('Angelique', [0, 20, 0]);
$lissy = new RatingModel('Lissy', [10, 0, 0]);
$mary = new RatingModel('Mary', [0, 0, 0]);
$max = new RatingModel('Max', [12, 0, 5]);
$simon = new RatingModel('Simon', [17, 2, 5]);
$manuel = new RatingModel('Manuel', [17, 8, 10]);
$betty = new RatingModel('Betty', [16, 9, 5]);
$sally = new RatingModel('Sally', [15, 10, 4]);
$peter = new RatingModel('Peter', [16, 9, 1]);
$stylists = [$nanda, $angelique, $lissy, $mary, $max, $simon, $manuel, $betty, $peter, $sally];
$relativeToClient = [];
foreach ($stylists as $stylist) {
$relativeToClient[] = [
'stylist' => $stylist->getName(),
'distance' => $stylist->distanceToModel($customer),
'score' => $stylist->scoreToModel($customer)
];
}
echo '<pre>';
print_r($stylists);
echo '<hr>';
print_r($customer);
echo '<hr>';
print_r($relativeToClient);
echo '<hr>from best fit to worst (low score means low delta)<hr>';
$results = array_column($relativeToClient, 'score', 'stylist');
asort($results);
print_r($results);
echo '</pre>';
Right below are the results (lower values are better):
Array
(
[Peter] => 0.067936622048676
[Sally] => 0.1700528000819
[Betty] => 0.20548046676563
[Manuel] => 0.35225222874108
[Simon] => 0.3942292057505
[Max] => 0.50765762377392
[Nanda] => 0.56568542494924
[Lissy] => 0.56568542494924
[Mary] => 0.7211102550928
[Angelique] => 0.84852813742386
)
If we look at the two best fitting stylists we notice, that Peter wins over Sally, because Sally has more Products with a different style.
Sally: [15, 10, 4]
Peter: [16, 9, 1]
You may also notice, that Nanda and Lissy have the same score:
Nanda: [20, 0, 0]
Lissy: [10, 0, 0]
// relatively, for both => [1.00, 0.00, 0.00]
They are both regarded equally fitting. Nanda has 5 products more and Lissy has 5 products less of the first style, but it does not matter, because they both only supply one style and this it what matters: How far they are away from the ideal which is the customer-style.
You could also implement the logic so that you have no bias factor and be more strict when it comes to the comparison. In this case you may want to exclude some of the params.
E.g. just comparing [15, 10] and [16, 9] - in this case Sally would actually win, because she has no delta to the customer when it comes to preferences:
Sally:
[
style1 => 0.60,
style2 => 0.40
]
Peter:
[
style1 => 0.64,
style2 => 0.36
]
Application to Distribute Stock between Warehouses
I have two arrays,
One has list of Warehouses along with the Current Quantity: (This can be Dynamic with one or more locations)
[
['location_name' => 'Toronto', 'current_qty' => 3],
['location_name' => 'Mississauga','current_qty' => 7],
['location_name' => 'London', 'current_qty' => 5],
]
The Other array has the Amount of Stock that would be Comming in:
[
'qty' => 5
]
And want to distribute the Qty among the locations so that the current qty of each of the locations would be near equal to each other. So want to return an array with the number that needs to be added to each location. Like: Here , of the 5, 3 went to Toronto and 2 to London. So it can be seen then after the nearest equalization, rest of the distribution can be done randomly.
[
['location_name' => 'Toronto', 'add_qty' => 3],
['location_name' => 'Mississauga','add_qty' => 0],
['location_name' => 'London', 'add_qty' => 2],
]
And Just cannot figure out the logic of this algorithm. Would really appreciate any pointers. Many Thanks.
I would do it like this, not really sure about any performance issues. I don't know how big your data set is.
$locations = [
['location_name' => 'Toronto', 'current_qty' => 3, 'add_qty' => 0],
['location_name' => 'Mississauga', 'current_qty' => 7, 'add_qty' => 0],
['location_name' => 'London', 'current_qty' => 5, 'add_qty' => 0],
];
$supplies = 5;
// This function sorts locations, by comparing the sum of the current quantity and the quantity the location will get.
$locationsByQuantityAscending = function ($locationA, $locationB) {
return ($locationA['current_qty'] + $locationA['add_qty']) - ($locationB['current_qty'] + $locationB['add_qty']);
};
// Sort the locations, getting the ones with the lowest quantity first.
usort($locations, $locationsByQuantityAscending);
// Keep dividing, until we're out of supplies
while ($supplies > 0) {
$locations[0]['add_qty']++; // Add one to the location with the lowest supplies
$supplies--; // Decrease the supplies by one
usort($locations, $locationsByQuantityAscending); // Sort the locations again.
}
print_r($locations);
At the end, this will output:
Array
(
[0] => Array
(
[location_name] => Toronto
[current_qty] => 3
[add_qty] => 3
)
[1] => Array
(
[location_name] => London
[current_qty] => 5
[add_qty] => 2
)
[2] => Array
(
[location_name] => Mississauga
[current_qty] => 7
[add_qty] => 0
)
)
If you really need to be performant, you could also just sort the locations once by their current quantity. Then keep adding to the first location, until its stock will be higher than the second location. Then, until the quantity at the second location is higher than the third location, add one to the first and second location, etc.
I think this will be more performant, since you don't have to sort all your locations X times (X being the number of supplies to divide). I'll leave that implementation to you.
Hint: have a look at recursive functions
If performance is critical, and the input data is usually bigger then shown here (e.g. a lot more locations or a lot more quantity to distribute). You might want to consider using SplMinHeap:
For example:
<?php
$helper = new class extends SplMinHeap
{
public function compare($a, $b): int
{
return ($b['current_qty'] + $b['add_qty']) <=> ($a['current_qty'] + $a['add_qty']);
}
};
$locations = [
['location_name' => 'Toronto', 'current_qty' => 3, 'add_qty' => 0],
['location_name' => 'Mississauga', 'current_qty' => 7, 'add_qty' => 0],
['location_name' => 'London', 'current_qty' => 5, 'add_qty' => 0],
];
foreach ($locations as $entry) {
$helper->insert($entry);
}
$qty = 10000;
while ($qty-- > 0) {
$min = $helper->extract();
$min['add_qty']++;
$helper->insert($min);
}
print_r(iterator_to_array($helper));
https://3v4l.org/nDOY8
I have two arrays which I can get after form submit:
$product_id = $request->get('product_id');
// [1, 3, 4]
$quantity = $request->get('quantity');
// [5, 1, 2]
Now I want to submit this arrays into database where I want to pick the purchase_price from product database. I'm not sure how to assign product_id to product_quantity (index 0 to 0, 1 to 1, 2 to 2) and store into database.
Sample data to store into carts:
[1 5 120 ],
[3 1 230 ],
[4 2 340 ],
foreach ($product_id as $product)
{
}
DB::table('carts')->insert(
['product_id' => '',
'quantity' => 0,
'purchase_price' =>
]
);
Just for clarification:
product_id and quantity come from dynamic input box means number of product_id and quantity are same but it could be n times as user wanted. So I store it as arrays.
Now from this array I wanted to store it in database where I want to store with product_id with quantity.
Lets give you some suggetions:
If you have below array - if not then make it array like below:
$dataset = [
0 => [
'product_id' => 1,
'quantity' => 5,
'purchase_price' => 120,
],
1 => [
'product_id' => 3,
'quantity' => 1,
'purchase_price' => 230,
],
2 => [
'product_id' => 4,
'quantity' => 2,
'purchase_price' => 340,
]
];
Now you have to write INSERT query for this:
$result = Cart::insert($dataSet);
if ($result)
return true;
else
return false;
You will get an idea how to do it after seeing above code...good luck
Please check out this sample.
you can parse 2d array and convert it to json to store in the database then decode back:
$product_id = [1,2,3];
// [1, 3, 4]
$quantity = [5,1,2];
// [5, 1, 2]
$output=[120,230,340];
$out=[];
for ($i=0; $i < count($product_id); $i++) {
$out[$i]=[$product_id[$i],$quantity[$i],$output[$i]];
}
dd(json_encode($out));
output:
"[[1,5,120],[2,1,230],[3,2,340]]"
You can use
foreach ($product_id as $key=>$product)
{
//select purchase price from table by using the $product value
$purchase_price = *your select code here*
DB::table('carts')->insert([
'product_id' => $product,
'quantity' => $quantity[$key],
'purchase_price' => $purchase_price
]);
}
Let me know if not works
I am using bootstrap progress bar to display stats.
I want to know how many scored 10 points out of total no of rows.
$q="SELECT * FROM users";
$r = mysqli_query($con,$q);
$total = mysqli_num_rows($r);
Name email score time
Hello abc#gmail.com 10 15
Hello abc#gmail.com 58 10
Test def#gmail.com 10 12
Stack xyz#gmail.com 90 20
Test def#gmail.com 50 40
$q="SELECT COUNT(Name) FROM users WHERE score='10' ";
You can use this simple query to get it done :-
SELECT COUNT(name), score
FROM users
GROUP BY score
This would get the total for each individual score (10,9,8).
If you want total only for value 10. Use this instead.
SELECT COUNT(name), score
FROM users
WHERE score=10
This will loop through the $total array and assign all the values less than 10 into an array then loop through that array to print out how many out of total for each less than 10.
$total = [
0 => [
'name' => 'Hello',
'email' => 'abc#gmail.com',
'score' => 10,
'time' => 15
],
1 => [
'name' => 'Hello',
'email' => 'abc#gmail.com',
'score' => 58,
'time' => 10
],
2 => [
'name' => 'Test',
'email' => 'def#gmail.com',
'score' => 10,
'time' => 12
],
3 => [
'name' => 'Stack',
'email' => 'xyz#gmail.com',
'score' => 90,
'time' => 20
],
4 => [
'name' => 'Test',
'email' => 'def#gmail.com',
'score' => 50,
'time' => 40
],
];
$sizeOfArray = count($total);
$arrayOfScoresLessThanTen = [];
foreach($total as $value) {
if($value['score'] <= 10) {
if(isset($arrayOfScoresLessThanTen[$value['score']])) {
$arrayOfScoresLessThanTen[$value['score']] ++;
}
else {
$arrayOfScoresLessThanTen[$value['score']] = 1;
}
}
}
foreach($arrayOfScoresLessThanTen as $key => $scoresLessThanTen) {
echo 'Score: '. $key . ' number scored out of total: '. $scoresLessThanTen . '/'.$sizeOfArray;
}
This has been tested and works with the array $total that I created to match the one you had in your table. I added my $total array so that you can test it for yourself.