Related
I am cracking my brain and can't find a good solution for my problem. I am trying to design a system that I can use for batch picking in our order system.
The point is that from a set of orders I want to pick 6 orders that are most equal to each other. In our warehouse most orders are them so we can safe a lot of time by picking some orders at the same time.
Assume I have the following array:
<?php
$data = [
156 => [
1,
2,
7,
9,
],
332 => [
3,
10,
6
],
456 => [
1,
],
765 => [
7,
2,
10,
],
234 => [
1,
9,
3,
6,
],
191 => [
7,
],
189 => [
7,
6,
3,
],
430 => [
10,
9,
1,
],
482 => [
1,
2,
7,
],
765 => [
1,
5,
9,
]
];
?>
The array key is the order id, and the values are the product ID's it contains. If I want to pick the top 3 orders which look at much like each other, where do I start?
Any help would be much appreciated!
1. Step
Sort productId inside order (ASC)
2. Step
In loop check difference (array_diff) in each order to each other.
Create array with defference. For example:
$diff = [
'156' => [ //order id
'234' => 4, // with order 234 has 4 differences
'332' => 7, // with order 332 has 7 differences
// and so on...
],
]
3. Step
Order $diff by ASC and receive order with less differences.
Improvement
Also you could add total size of products in order for compare with difference. For example, If you have an order with 100 products and 10 diffs - it's better than order with 10 products and 9 diffs.
Here is what i would do if I had the problem :
$topOrders = [];
foreach($data as $value):
foreach($value as $order):
if(isset($$order)):
$$order++;
else:
$$order = 1;
endif;
$topOrders[$order] = $$order;
endforeach;
endforeach;
print_r($topOrders);
In $topOrders, you have an array that contains as key the ID, and as value you got the number of orders. All you have to do is to sort your array to get your top 3.
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
Right now I have an array of objects in PHP, and some of them have a duplicate value for "id". I'm trying to remove the duplicates but I want to keep the one with the lowest value for "qty".
I know how you'd normally remove duplicate whole objects from an array, but not only if one value is a duplicate and how to keep the lower of another value.
Example:
[
{
id: 1,
qty: 200
},
{
id: 1,
qty: 190
},
{
id: 2,
qty: 10
},
{
id: 2,
qty: 12
},
{
id: 2,
qty: 10
},
{
id: 3,
qty: 5
},
{
id: 4,
qty: 5
},
{
id: 4,
qty: 2
},
]
What I want to end up with would be..
[
{
id: 4,
qty: 2
},
{
id: 3,
qty: 5
},
{
id: 2,
qty: 10
},
{
id: 1,
qty: 190
}
]
Is this possible?
That looks almost like JSON, so assuming you $array = json_decode($json, true) to an associative array:
array_multisort(array_column($array, 'qty'), SORT_DESC, $array);
$result = array_column($array, null, 'id');
Extract an array of qty and sort that descending, sorting the original by that
Extract from that an array with id as the key
Extracting with array_column() will cause the last id key to overwrite the previous ones. The last one will be the one with the lowest qty since it was sorted DESCending.
If you need to get it back to a JSON object, then just re-index:
$json = json_encode(array_values($result));
AbraCadaver came up with such a good answer, but I worked hard to come up with mine, so I want to share it in case it is useful for someone. If anything, it may provide useful for an expanded or more complex array. I went the route of creating a nested loop. Here is the code:
$newArray = [];
for ($i = 0; $i < count($myArray); $i++)
{
$id_column = array_column($newArray, 'id');
$qty_column = array_column($newArray, 'qty');
if (!in_array($myArray[$i]['id'],$id_column)) {
array_push($newArray, $myArray[$i]);
}
else {
$id_pos = array_search($myArray[$i]['id'],$id_column);
if ($myArray[$i]['qty'] < $qty_column[$id_pos])
{
array_splice($newArray,$id_pos,1,$myArray[$i]);
}
}
}
Basically I create a new empty array. I loop through each element of the original array to see if it's in the new array. If not, I add it, and if it is already in the new array, then I check the new Array to see if the qty for that id is higher, if so, I splice in the current row.
It is not necessary to involve a sorting algorithm -- that would only negatively impact the script performance.
If a row's id is not yet presented in the output array, push it into the output array with a temporary first level key. If the row's id is already in the output array, but has a lesser qty, then overwrite the row in the output array.
This task never needs more than one loop. If you want to remove the temporary first level keys, then call array_values() after looping.
Code: (Demo)
$data = [
['id' => 1, 'qty' => 200],
['id' => 1, 'qty' => 190],
['id' => 2, 'qty' => 10],
['id' => 2, 'qty' => 12],
['id' => 2, 'qty' => 10],
['id' => 3, 'qty' => 5],
['id' => 4, 'qty' => 5],
['id' => 4, 'qty' => 2],
];
$result = [];
foreach ($data as $row) {
if (!isset($result[$row['id']]) || $row['qty'] < $result[$row['id']]['qty']) {
$result[$row['id']] = $row;
}
}
var_export(array_values($result));
Output:
array (
0 =>
array (
'id' => 1,
'qty' => 190,
),
1 =>
array (
'id' => 2,
'qty' => 10,
),
2 =>
array (
'id' => 3,
'qty' => 5,
),
3 =>
array (
'id' => 4,
'qty' => 2,
),
)
Here is one of the possible answers, assuming $your_input contains your data as a string…
Method 1
<?php
$your_input=preg_replace('#\s*\{\s*id\s*:\s*([^,]+?),\s*qty\s*:\s*([^,\}]+?)\s*\}\s*(,)?#','{"id":"$1","qty":"$2"}$3',$your_input);
$your_input=json_decode($your_input,true);
$result=[];
foreach($your_input as $a)
if (!array_key_exists($a['id'],$result) or $a['qty']<$result[$a['id']]['qty'])
{
$result[$a['id']]['id']=$a['id'];
$result[$a['id']]['qty']=$a['qty'];
}
$result=json_encode(array_values($result),JSON_PRETTY_PRINT);
echo '<pre>';
print_r($result);
Method 2
<?php
$your_input=str_replace([' ',':'],['"','":'],$your_input);
$your_input=json_decode($your_input,true);
usort($your_input,function($a,$b){return $a['qty']==$b['qty']?0:($a['qty']>$b['qty']?-1:1);});
foreach($your_input as $b) $result[$b['id']]=['id'=>$b['id'],'qty'=>$b['qty']];
$result=json_encode(array_values($result),JSON_PRETTY_PRINT);
echo '<pre>';
print_r($result);
After Method 1 or Method 2 the $result variable should contains the following data string…
[
{
"id": 1,
"qty": 190
},
{
"id": 2,
"qty": 10
}
]
I have a table of data that contains a material, the amount of the material stored, and the date it was collected (below is an example of the data):
|------------|--------------|-----------------|
| waste_type | total_weight | collection_date |
|------------|--------------|-----------------|
| Wood | 50 | 2014-05-24 |
| Wood | 75 | 2014-06-25 |
| Metal | 150 | 2014-06-25 |
| Plastic | 20 | 2014-07-10 |
|------------|--------------|-----------------|
Using the following query:
$materialsCollected = $dm->createQuery('
SELECT SUM(efu.totalWeight) AS totalWeight, efu.wasteType, efu.collectionDate
FROM CoreBundle:EnviroFiguresUpload efu
GROUP BY efu.collectionDate
ORDER BY efu.collectionDate DESC'
);
$matColl = $materialsCollected->getResult();
Which is then put in to an array by Symfony2 like this:
Array
(
[0] => Array
(
[totalWeight] => 50
[wasteType] => Wood
[collectionnDate] => 2014-05-24
)
[1] => Array
(
[totalCO2] => 75
[wasteType] => Wood
[collectionnDate] => 2014-05-24
)
[2] => Array
(
[totalCO2] => 150
[wasteType] => Metal
[collectionnDate] => 2014-05-24
)
[3] => Array
(
[totalCO2] => 20
[wasteType] => Plastic
[collectionnDate] => 2014-05-24
)
)
Now this data is being passed to Flot.js to display a stacked bar graph. The example code I'm using is this:
<script>
init.push(function () {
// Visits Chart Data
var visitsChartData = [{
label: 'Visits',
data: [
[6, 1300], [7, 1600], [8, 1900], [9, 2100], [10, 2500], [11, 2200], [12, 2000], [13, 1950], [14, 1900], [15, 2000]
]
}, {
label: 'Returning Visits',
data: [
[6, 750], [7, 600], [8, 550], [9, 600], [10, 800], [11, 900], [12, 800], [13, 850], [14, 830], [15, 1000]
],
filledPoints: true // Fill points
}, {
label: 'New Visits',
data: [
[6, 300], [7, 450], [8, 250], [9, 100], [10, 400], [11, 300], [12, 200], [13, 850], [14, 830], [15, 1000]
],
filledPoints: true // Fill points
}];
// Init Chart
$('#jq-flot-bars').pixelPlot(visitsChartData, {
series: {
bars: {
show: true,
barWidth: .9,
align: 'center'
}
},
xaxis: { tickDecimals: 2 },
yaxis: { tickSize: 1000 }
}, {
height: 205,
tooltipText: "y + ' visitors at ' + x + '.00h'"
});
});
</script>
<!-- / Javascript -->
<div class="panel">
<div class="panel-heading">
<span class="panel-title">CO2 Savings</span>
</div>
<div class="panel-body">
<div class="graph-container">
<div id="jq-flot-bars" class="graph"></div>
</div>
</div>
</div>
The problem I have is that I want to group the data in to months, and display them in the Flot.js. However, I'm not sure how to group them up correctly. So each bar will be made up of each material, and there will be a bar for each month.
How can I group the data by month, and then be able to pass it to the Flot.js graph?
You need to install beberlei/DoctrineExtensions bundle and enable MONTH and YEAR functions for doctrine. Take a look at my answer here. Then you can easily group and filter your result by month
SELECT SUM(efu.totalWeight) AS totalWeight, efu.wasteType, efu.collectionDate
FROM CoreBundle:EnviroFiguresUpload efu
GROUP BY MONTH(efu.collectionDate)
ORDER BY YEAR( efu.collectionDate ) DESC, efu.collectionDate DESC
Another solution is to group result on client(in javascript) before you pass it to Flot.js
You could group it directly in the query like this. Then you would not have change the rest of the code towards the frontend much.
$materialsCollected = $dm->createQuery('
SELECT
SUM(efu.totalWeight) AS totalWeight,
EXTRACT(YEAR_MONTH FROM efu.collectionDate) AS yearAndMonth,
efu.wasteType
FROM CoreBundle:EnviroFiguresUpload efu
GROUP BY yearAndMonth, efu.wasteType
ORDER BY yearAndMonth DESC
');
By the way, you forgot to additionally group by wasteType... :)
The mysql function I use is documented here:
http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_extract
Suppose you have directly passed the query result into javascript by serializing in with json_encode and you got this:
var raw = [
{
totalWeight: 50,
wasteType: 'Wood',
collectionDate: '2014-05-24'
},
{
totalWeight: 60,
wasteType: 'Wood',
collectionDate: '2014-06-15'
},
{
totalWeight: 35,
wasteType: 'Metal',
collectionDate: '2014-05-24'
},
{
totalWeight: 70,
wasteType: 'Metal',
collectionDate: '2014-06-03'
},
{
totalWeight: 30,
wasteType: 'Plastic',
collectionDate: '2014-05-24'
},
{
totalWeight: 110,
wasteType: 'Plastic',
collectionDate: '2014-06-12'
},
];
You can now group this array by month and convert it into correct chart data:
data = {};
for (var i = 0; i < raw.length; i++) {
var wasteType = raw[i]['wasteType'],
totalWeight = raw[i]['totalWeight'],
date = new Date(raw[i]['collectionDate']),
month = date.getMonth() + 1;
data[wasteType] = data[wasteType] || {};
data[wasteType][month] = data[wasteType][month] || 0;
data[wasteType][month] += totalWeight;
}
var result = [];
for (var label in data) {
if (data.hasOwnProperty(label)) {
var item = {};
item.label = label;
item.data = [];
for (month in data[label]) {
if (data[label].hasOwnProperty(month)) {
item.data.push([+ month, data[label][month]]);
}
}
result.push(item);
}
}
Variable result is ready to be passed to flot library:
[{
'label' : 'Wood',
'data' : [[5, 50], [6, 60]]
},{
'label' : 'Metal',
'data' : [[5, 35], [6, 70]]
},{
'label' : 'Plastic',
'data' : [[5, 30], [6, 110]]
}]
You can group the values directly in your SQL query:
SELECT
MONTH(efu.collectionDate) as collectionMonth,
SUM(efu.totalWeight) AS totalWeight,
efu.wasteType as wasteType
FROM
CoreBundle:EnviroFiguresUpload efu
GROUP BY
collectionMonth,
wasteType
ORDER BY
collectionMonth
This returns an array similar to this:
Array
(
[0] => Array
(
[collectionMonth] => 5
[totalWeight] => 50
[wasteType] => Wood
)
[..]
)
Then you simply group all values by type and map them so you can use them directly with plot.js without having to format the data yet again on the Javascript side.
<?php
$sum = array();
$plot = array();
foreach($rows as $row)
{
$type = $row['wasteType'];
$month = $row['collectionMonth'];
$total = $row['totalWeight'];
if(!isset( $sum[$type] )) $sum[$type] = array();
$sum[$type][] = array($month, $total);
}
foreach($sum as $label => $data)
{
$plot[] = array(
'label' => $label,
'data' => $data,
);
}
And then you can simply json_encode the $plot array and use it with flot.js:
json_encode($plot) =>
[
{
"label": "Plastic",
"data": [
["7","20"]
]
},
{
"label": "Metal",
"data": [
["6","150"]
]
},
{
"label": "Wood",
"data": [
["6","75"],["5","50"]
]
}
]
Just keep in mind that this aggregates all months, so if you only want display the data for a certain year you should add a constraint to your sql query:
[..] WHERE YEAR(efu.collectionDate) = '2014' GROUP BY [..]
I need to update multiple embedded docs in mongo using PHP. My layout looks like this:
{
_id: id,
visits : {
visitID: 12
categories: [{
catagory_id: 1,
name: somename,
count: 11,
duration: 122
},
{
catagory_id: 1,
name: some other name,
count: 11,
duration: 122
},
{
catagory_id: 2,
name: yet another name,
count: 11,
duration: 122
}]
}
}
The document can have more than one visit too.
Now i want to update 2 categories, one with id=1 and name=somename and the other with id=1 and name=some_new_name. Both of them should inc "count" by 1 and "duration" by 45.
First document exists, but second does not.
Im thinking of having a function like this:
function updateCategory($id, $visitID,$category_name,$category_id) {
$this->profiles->update(
array(
'_id' => $id,
'visits.visitID' => $visitID,
'visits.categories.name' => $category_name,
'visits.categories.id' => $category_id,
),
array(
'$inc' => array(
'visits.categories.$.count' => 1,
'visits.categories.$.duration' =>45,
),
),
array("upsert" => true)
);
}
But with this i need to call the function for each category i will update. Is there any way to do this in one call?
EDIT:
Changed the layout a bit and made "categories" an object instead of array. Then used a combination of "category_id" and "category_name" as property name. Like:
categories: {
1_somename : {
count: 11,
duration: 122
},
1_some other name : {
count: 11,
duration: 122
},
2_yet another name : {
count: 11,
duration: 122
},
}
Then with upsert and something like
$inc: {
"visits.$.categories.1_somename.d": 100,
"visits.$.categories.2_yet another name.c": 1
}
i can update several "objects" at a time..
Mongodb currently not supporting arrays multiple levels deep updating (jira)
So following code will not work:
'$inc' => array(
'visits.categories.$.count' => 1,
'visits.categories.$.duration' => 123,
),
So there is some solutions around this:
1.Load document => update => save (possible concurrency issues)
2.Reorganize your documents structure like this(and update using one positional operator):
{
_id: id,
visits : [{
visitID: 12
}],
categories: [{
catagory_id: 1,
name: somename,
count: 11,
duration: 122,
visitID: 12
}]
}
}
3.Wait for multiple positional operators support (planning in 2.1 version of mongodb).
4.Reorganize your documents structure somehow else, in order to avoid multiple level arrays nesting.