Related
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
]
So im trying to send an object array to a js function anytime I push a button.
<button onclick="actualizarProcesos(<?php echo json_encode($p_array)?>);">X</button>
I've made sure my json isnt sending any weird characters as it is mostly int except for the object attribute "name" which is string.
That function is in a different js file:
function actualizarProcesos(p_array){
var p = JSON.parse(p_array);
}
At the moment I'm trying to send make sure the function is receiving the data but it keeps throwing the error Uncaught SyntaxError: Unexpected end of inputSo I'm stuck trying to find out how to fix the error.
I plan afterwards on sending that array to another php file using ajax, something like this:
$.ajax({
type: "POST",
url: "post.php",
data: JSON.stringify(values),
success: function(data){
alert(data)
}
});
This is the full json I'm trying to send
[{"name":"A","t_arrival":7,"t_est":25,"state":1,"pages":5,"mem_data":[[1,8,13,5,0,1],[0,0,0,0,0,0],[1,11,17,3,1,1],[1,12,16,4,0,1],[0,0,0,0,0,0]],"t_rem":25,"t_wait":0,"rem_quantum":0},{"name":"B","t_arrival":6,"t_est":13,"state":2,"pages":4,"mem_data":[[0,0,0,0,0,0],[1,9,16,5,0,1],[1,7,14,6,1,0],[0,0,0,0,0,0]],"t_rem":13,"t_wait":0,"rem_quantum":0},{"name":"C","t_arrival":8,"t_est":37,"state":3,"pages":3,"mem_data":[[1,9,12,2,0,0],[0,0,0,0,0,0],[1,13,21,7,0,1]],"t_rem":37,"t_wait":0,"rem_quantum":0}]
I've made sure my json isnt sending any weird characters as it is mostly int except for the object attribute "name" which is string.
Strings in JSON are delimited with " characters.
<button onclick="actualizarProcesos(<?php echo json_encode($p_array)?>);">X</button>
Your HTML attribute value is delimited with " characters.
The " in the data will cut off the attribute value.
You need to encode your data (using htmlspecialchars) for embedding in HTML:
<?php echo htmlspecialchars(json_encode($p_array)); ?>
you can do it by this way
<p id="test" style="display:none"><?php echo json_encode($p_array); ?></p>
<button onclick="actualizarProcesos(test)" id="">X</button>
<script src="https://code.jquery.com/jquery-1.9.1.min.js"></script>
<script>
function actualizarProcesos(test){
var p_array = $('#test').html();
var p = JSON.parse(p_array);
alert(p);
}
Tested working fine. Hope this help you.
<?php
$p_array = [
[
"name" => "A",
"t_arrival" => 7,
"t_est" => 25,
"state" => 1,
"pages" => 5,
"mem_data" => [
[
1,
8,
13,
5,
0,
1
],
[
0,
0,
0,
0,
0,
0
],
[
1,
11,
17,
3,
1,
1
],
[
1,
12,
16,
4,
0,
1
],
[
0,
0,
0,
0,
0,
0
]
],
"t_rem" => 25,
"t_wait" => 0,
"rem_quantum" => 0
],
[
"name" => "B",
"t_arrival" => 6,
"t_est" => 13,
"state" => 2,
"pages" => 4,
"mem_data" => [
[
0,
0,
0,
0,
0,
0
],
[
1,
9,
16,
5,
0,
1
],
[
1,
7,
14,
6,
1,
0
],
[
0,
0,
0,
0,
0,
0
]
],
"t_rem" => 13,
"t_wait" => 0,
"rem_quantum" => 0
],
[
"name" => "C",
"t_arrival" => 8,
"t_est" => 37,
"state" => 3,
"pages" => 3,
"mem_data" => [
[
1,
9,
12,
2,
0,
0
],
[
0,
0,
0,
0,
0,
0
],
[
1,
13,
21,
7,
0,
1
]
],
"t_rem" => 37,
"t_wait" => 0,
"rem_quantum" => 0
]
];
$p_array = json_encode($p_array);
?>
<button onclick='actualizarProcesos(<?php echo $p_array ?>)' >X</button>
<script>
function actualizarProcesos(arr){
console.log(arr);
}
</script>
I have a table with a json column cast as array. The Schema creation has the column defined like this:
$table->json('row_ids');
In my class:
protected $casts = [
'row_ids' => 'array',
];
I generate the data in this array / column by getting every row id from another table like this:
$id_array = DB::table($table->name)->pluck('api_id')->toArray();
TableChannelRow::create([
'table_api_id' => $table->api_id,
'channel_api_id' => $channel->api_id,
'row_ids' => $id_array,
]);
When I dd a collection record I can see the columns in the target table OK, with one of the columns containing an array as expected:
#attributes: array:4 [▼
"api_id" => 2
"table_api_id" => 1
"channel_api_id" => 6
"row_ids" => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, ▶"
]
When I check in MySQLWorkbench the data looks like this:
In another controller I want to add or remove entries from the array in this column like this:
$table_channel_row = TableChannelRow::where('table_api_id', '=', $rowId)
->where('channel_api_id', '=', $channelId)
->first();
$row_ids = $table_channel_row->row_ids;
if ($is_checked == 'yes') {
// Add value to array if it does not already exist
if (!in_array($row_id, $row_ids)) {
array_push($row_ids, $row_id);
}
} else {
// Remove value from array
$row_id_array[] = $row_id;
$row_ids = array_diff($row_ids, $row_id_array);
}
$table_channel_row->update([
'row_ids' => $row_ids,
]);
Now the data in MySQLWorkbench looks like this:
Why does it get stored looking like a PHP array in the first instance, then later on update it gets stored as a json object?
Additionally the remove PHP code is working, yet the add is not, though it does not trigger an exception (you can see the first value is removed in the second image, but I cannot find it in the object in MySQL when I trigger the code to add it)
What did I miss? Thanks!
The reason why it's saving differently is because array_diff returns an associative array whereas your initial data is an indexed array. Take the following for example:
$ids = [1, 2, 3, 4, 5];
$ids2 = [1, 2, 3];
Then if you perform an array_diff($ids, $ids2), it would return the following:
[
3 => 4,
4 => 5
]
So if you want to save as the same format as your initial one, you have to retrieve the values of the array using array_values:
$row_ids = array_values(array_diff($row_ids, $row_id_array));
For instance, I have an array with N elements, and I need that it have M elements where M is multiple of O. Example:
[signature: array array_fill_multiple(array, int o, mixed filler = null)]
$array = [ 1, 2 ];
array_fill_multiple($array, 1); // => [ 1, 2 ]
array_fill_multiple($array, 2); // => [ 1, 2 ]
array_fill_multiple($array, 3); // => [ 1, 2, null ]
array_fill_multiple($array, 4); // => [ 1, 2, null, null ]
$array = [ 1, 2, 3 ];
array_fill_multiple($array, 1); // => [ 1, 2, 3 ]
array_fill_multiple($array, 2); // => [ 1, 2, 3, null ]
array_fill_multiple($array, 3); // => [ 1, 2, 3 ]
array_fill_multiple($array, 4); // => [ 1, 2, 3, null ]
$array = [ 1, 2, 3, 4 ];
array_fill_multiple($array, 5, 0); // => [ 1, 2, 3, 4, 0 ]
I can do it with a for, but I guess that using native methods are possible, are not?
Edit
To explain better what I want, it need result in an array with multiples of O elements. So if I like that this array contains multiples of 5, it need result in an array with 0, 5, 10, 15, 20, 25... elements (zero included, if empty).
If my array have 15 elements, and I expect multiple of 2, it'll fit to next multiple of 2 after or equal 15, or be, 16. So only 1 padding will be created.
If my array have 3 elements and I expect multiple of 5, it'll fit to next multiple of 5, after or equal to 3, or be, the own 5. So 2 paddings will be created.
If my array have 10 elements and I expect multiple of 10, it'll fit to next multiple of 10, after or igual to 10, or be, the own 10. So none padding will be created, because my array yet is multiple of 10.
If my array is empty, it'll return an empty array.
I guess that array_pad() will help. Just need calculate the second argument to know what is the next multiple of O based on array count.
Something like this
$a = [1, 2, 3];
$multiple = 5;
$size = sizeof($a);
// first way
var_dump(array_pad($a, ceil($size / $multiple) * $multiple, null));
// second way
var_dump(array_pad($a, $size + ($size % $multiple ? $multiple - $size % $multiple : 0), null));
Second one is more accurate than first one. Let's suppose that you have array with 10000000000000001 items (on 64 system). Now you have to pad with multiplier 11.
$size = 10000000000000001 * 11;
$multiple = 11;
var_dump($size);
// int(110000000000000011)
// first way
ini_set('precision', 18);
var_dump(ceil($size / $multiple) * $multiple);
// double(110000000000000000)
// second way
var_dump($size + ($size % $multiple ? $multiple - $size % $multiple : 0));
// int(110000000000000011)
Now you see that first way produces wrong value because float values has less precision than int.
Basically you just replace "array_fill_multiple" with "array_pad" and it'll work :)
$array = [ 1, 2 ];
array_pad($array, 1, null); // => [ 1, 2 ]
array_pad($array, 2, null); // => [ 1, 2 ]
array_pad($array, 3, null); // => [ 1, 2, null ]
array_pad($array, 4, null); // => [ 1, 2, null, null ]
$array = [ 1, 2, 3 ];
array_pad($array, 1, null); // => [ 1, 2, 3 ]
array_pad($array, 2, null); // => [ 1, 2, 3, null ]
array_pad($array, 3, null); // => [ 1, 2, 3 ]
array_pad($array, 4, null); // => [ 1, 2, 3, null ]
$array = [ 1, 2, 3, 4 ];
array_pad($array, 5, 0); // => [ 1, 2, 3, 4, 0 ]
I'm trying to calculate the volume of each box but I keep coming across errors and I don't know how to fix it. Everything seem correct and I followed how the book's example.
This is the error it gave me in this code:
"Notice: Undefined offset: 0 in C:\wamp\www\BoxArray.php on line 16"
<?php
$BoxMeasurements = array("sBox" => array("length" => 12, "width" => 10, "depth" => 2.5),
"mBox" => array("length" => 30, "width" => 20, "depth" => 4),
"lBox" => array("length" => 60, "width" => 40, "depth" => 11.5));
$BoxMeasurements = array_slice($BoxMeasurements, 0, 3);
echo "The box sizes are:";
for($i = 0; $i < count($BoxMeasurements); ++$i)
{
echo "$BoxMeasurements[$i]";
}
?>
When I tried doing it the other way I got this error:
"Parse error: syntax error, unexpected '=', expecting ')' in C:\wamp\www\BoxArray.php on line 8"
<?php
$sBox = array("length" => 12, "width" => 10, "depth" = 2.5);
$mBox = array("length" => 30, "width" => 20, "depth" = 4);
$lBox = array("length" => 60, "width" => 40, "depth" => 11.5);
$dimension = array($sBox, $mBox, $lBox);
echo "$dimension[0][0]";
?>
Is there a special way to call the variable/array name?
$BoxMeasurements is declared as an associative array, which means you should access its values with the keys you defined in the declaration: "sBox", "mBox" and "lBox".
In order to iterate over this kind of arrays you can use the foreach form:
<?php
$BoxMeasurements = array("sBox" => array("length" => 12, "width" => 10, "depth" => 2.5),
"mBox" => array("length" => 30, "width" => 20, "depth" => 4),
"lBox" => array("length" => 60, "width" => 40, "depth" => 11.5));
echo "<pre>";
echo "The box sizes are:\n";
foreach($BoxMeasurements as $name => $size)
{
$volume = $size['length'] * $size['width'] * $size['depth'];
echo " - $name: $volume\n";
}
echo "</pre>";
?>
OUTPUT
The box sizes are:
- sBox: 300
- mBox: 2400
- lBox: 27600
You seem not to understand difference between variable name and variable content, as indicated by using " around names. It's pointless. It should be
echo $BoxMeasurements[$i];
While assigning your array, correct syntax is:
key => value
while some of your "rows" are assigned just "key = value" which throws syntax error.
Also you try to access array by numerical indices, while your array does not use them. Use foreach to iterate the array:
foreach( $BoxMeasurements as $key=>$val ) {
echo $val;
}
Finally, you should be doing post increment in your for loop, not pre increment.
I strongly suggest to spend some time and go through some tutorials as you made too many elementary errors.