I'm trying to make a draw for a secret Santa.
I collect in a table the information of the person and the name of the person on whom it should not fall (to avoid couples).
However during my PHP loop I can't take into account my exclusions
foreach ($supplier as $sup){
$exclude = $sup['blacklist'];
$data = $recipient;
$temp = array_diff($data[], array($exclude));
echo $temp[rand(0, sizeOf($temp))];
foreach ($recipient as $key=>$recip){
if ($sup['surname'] !== $recip['surname']){
$result[] = ['recipient' => $recip, 'supplier' => $sup];
unset($recipient[$key]);
}
}
}
How can I take into account this blacklist please?
shuffle($supplier);
shuffle($recipient);
// dump($supplier, $recipient);
$result = [];
foreach ($supplier as $sup){
$assign = false;
dump($sup);
foreach ($recipient as $key=>$recip){
dump($recip['surname']);
if ($sup['surname'] !== $recip['surname'] && $sup['blacklist'] !== $recip['surname'] && $sup['surname'] !== $recip['blacklist']){
$result[] = ['recipient' => $recip, 'supplier' => $sup];
dump($sup['surname']);
unset($recipient[$key]);
$assign = true;
}
if ($assign === true){
break;
}
}
}
return $result;
}
this is my code with shuffle
Your solution is fine, but with that nested loop it's going to start behaving poorly as the list of participants grows. You can accomplish the task with a single array and two linear passes over it:
$people = [
[ 'name' => 'alice', 'blacklist' => ['bob'] ],
[ 'name' => 'bob', 'blacklist' => ['alice'] ],
[ 'name' => 'carl', 'blacklist' => ['david'] ],
[ 'name' => 'david', 'blacklist' => ['carl'] ],
[ 'name' => 'elise', 'blacklist' => ['frank'] ],
[ 'name' => 'frank', 'blacklist' => ['elise'] ],
[ 'name' => 'georg', 'blacklist' => ['herb'] ],
[ 'name' => 'herb', 'blacklist' => ['george'] ]
];
function wrap_index($count, $index) {
$cur = $index;
if( $index == 0 ) {
$prev = $count - 1;
$next = $index + 1;
} else if( $index == $count - 1) {
$prev = $index - 1;
$next = 0;
} else {
$prev = $index - 1;
$next = $index + 1;
}
return [$prev, $cur, $next];
}
function array_swap(&$array, $a, $b) {
$tmp = $array[$a];
$array[$a] = $array[$b];
$array[$b] = $tmp;
}
function santify($people) {
shuffle($people);
$count = count($people);
for( $i=0; $i<$count; ++$i ) {
list($prev, $cur, $next) = wrap_index($count, $i);
if( in_array($people[$cur]['name'], $people[$next]['blacklist']) ) {
printf("%s in blacklist for %s\n", $people[$cur]['name'], $people[$next]['name']);
array_swap($people, $cur, $prev);
}
}
$pairs = [];
for( $i=0; $i<$count; ++$i ) {
list($prev, $cur, $next) = wrap_index($count, $i);
$pairs[] = [ $people[$cur]['name'], $people[$next]['name'] ];
}
return $pairs;
}
foreach( santify($people) as $pair ) {
printf("%s\n", json_encode($pair));
}
Output:
david in blacklist for carl
elise in blacklist for frank
["david","georg"]
["georg","carl"]
["carl","bob"]
["bob","herb"]
["herb","elise"]
["elise","alice"]
["alice","frank"]
["frank","david"]
There is a caveat to this approach, though. It will only work with strict 1:1 blacklist arrangements. Once there is a love triangle [or quardrangle or above] or any polyamory, neither of our solutions is equipped for these extra dimensions.
Related
I am beginner in PHP.
I have this array:
$array = array(
['name' => 'project 1', 'url' => 'www.name1.com', 'photo' => '1.jpg'],
['name' => 'project 2', 'url' => 'www.name2.com', 'photo' => '2.jpg'],
['name' => 'project 3', 'url' => 'www.name3.com', 'photo' => '3.jpg'],
['name' => 'project 4', 'url' => 'www.name4.com', 'photo' => '4.jpg'],
['name' => 'project 5', 'url' => 'www.name5.com', 'photo' => '5.jpg'],
['name' => 'project 6', 'url' => 'www.name6.com', 'photo' => '6.jpg'],
)
I need get by function next and previous element from my array (if exist):
$next = next($actualUrl);
$previous = previous($actualUrl);
How can I make it?
this simple code will help you:
<?php
function next_elm ($array, $actualUrl) {
$i = 0;
while ( $i < count($array) && $array[$i]["url"] != $actualUrl ) $i++;
if ($i < (count($array) - 1)) {
return $array[$i+1];
} else if ($i == (count($array) - 1)) {
return $array[0]; // this is depend what you want to return if the url is the last element
} else {
return false; // there is no url match
}
}
function prev_elm ($array, $actualUrl) {
$i = 0;
while ( $i < count($array) && $array[$i]["url"] != $actualUrl ) $i++;
if ($i < (count($array)) && $i>0) {
return $array[$i-1];
} else if ($i == 0) {
return $array[count($array) - 1]; // this is depend what you want to return if the url is the first element
} else {
return false; // there is no url match
}
}
I prefer to iterate over any array via a foreach loop. if you want anything specific out of it just copy it into a tmp variable. for example:
$tmp_var = null;
foreach($array as $key => $value){
$tmp_var = $value['name'];
}
First find actual url, then use this index to find previous and next items. Also you should add checks if the current item is first or last element to avoid null pointer exception.
$curr = 0;
foreach($array as $value){
if($value['url'] == 'www.name2.com'){
break;
}
$curr += 1;
}
$previous = $array[$curr-1];
$next = $array[$curr+1];
I make a parser of items from DotA 2 user inventory in the Steam service. Every time I try to parse user data, I get an empty value:
{"success":true,"items":[]}, but there are items in my Steam inventory.
My function to parse items:
public function loadMyInventory() {
if(Auth::guest()) return ['success' => false];
$prices = json_decode(Storage::get('prices.txt'), true);
$response = json_decode(file_get_contents('https://steamcommunity.com/inventory/'.$this->user->steamid64.'/570/2?l=russian&count=5000'), true);
if(time() < (Session::get('InvUPD') + 5)) {
return [
'success' => false,
'msg' => 'Error, repeat in '.(Session::get('InvUPD') - time() + 5).' сек.',
'status' => 'error'
];
}
//return $response;
$inventory = [];
foreach($response['assets'] as $item) {
$find = 0;
foreach($response['descriptions'] as $descriptions) {
if($find == 0) {
if(($descriptions['classid'] == $item['classid']) && ($descriptions['instanceid'] == $item['instanceid'])) {
$find++;
# If we find the price of an item, then move on.
if(isset($prices[$descriptions['market_hash_name']])) {
# Search data
$price = $prices[$descriptions['market_hash_name']]*$this->config->curs;
$class = false;
$text = false;
if($price <= $this->config->min_dep_sum) {
$price = 0;
$text = 'Cheap';
$class = 'minPrice';
}
if(($descriptions['tradable'] == 0) || ($descriptions['marketable'] == 0)) {
$price = 0;
$class = 'minPrice';
$text = 'Not tradable';
}
# Adding to Array
$inventory[] = [
'name' => $descriptions['market_name'],
'price' => floor($price),
'color' => $this->getRarity($descriptions['tags']),
'tradable' => $descriptions['tradable'],
'class' => $class,
'text' => $text,
'classid' => $item['classid'],
'assetid' => $item['assetid'],
'instanceid' => $item['instanceid']
];
}
}
}
}
}
Session::put('InvUPD', (time() + 5));
return [
'success' => true,
'items' => $inventory
];
}
But should return approximately the following value:
{"success":true,"items":[{"classid":"2274725521","instanceid":"57949762","assetid":"18235196074","market_hash_name":"Full-Bore Bonanza","price":26}]}
Where my mistake?
First of all, you are iterating on descriptions for every assets, which is assets*descriptions iteration, it's quite a lot, but you can optimize this.
let's loop once for descriptions and assign classid and instanceid as object key.
$assets = $response["assets"];
$descriptions = $response["descriptions"];
$newDescriptions=[];
foreach($descriptions as $d){
$newDescriptions[$d["classid"]][$d["instanceid"]] = $d;
}
this will give as the ability to not loop over description each time, we can access the description of certain asset directly $newDescriptions[$classid][$instanceid]]
foreach($assets as $a){
if(isset($newDescriptions[$a["classid"]]) && isset($newDescriptions[$a["classid"]][$a["instanceid"]])){
$assetDescription = $newDescriptions[$a["classid"]][$a["instanceid"]];
$inventory = [];
if(isset($prices[$assetDescription["market_hash_name"]])){
$price = $prices[$assetDescription['market_hash_name']]["price"]*$this->config->curs;
$class = false;
$text = false;
if($price <= $this->config->min_dep_sum) {
$price = 0;
$text = 'Cheap';
$class = 'minPrice';
}
if(($assetDescription['tradable'] == 0) || ($assetDescription['marketable'] == 0)) {
$price = 0;
$class = 'minPrice';
$text = 'Not tradable';
}
$inventory["priceFound"][] = [
'name' => $assetDescription['market_name'],
'price' => floor($price),
'color' => $this->getRarity($assetDescription['tags']),
'tradable' => $assetDescription['tradable'],
'class' => $class,
'text' => $text,
'classid' => $a['classid'],
'assetid' => $a['assetid'],
'instanceid' => $a['instanceid']
];
}else{
$inventory["priceNotFound"][] = $assetDescription["market_hash_name"];
}
}
}
About your mistake:
are you Sure your "prices.txt" contains market_hash_name?
I don't see any other issue yet, operationg on the data you have provided in comment, I got print of variable $assetDescription. Please doublecheck variable $prices.
In my lottery project I have 5 tickets, in which you select numbers and buy. The thing is, you can only buy the tickets if you buy them in order... For example:
Ticket 1 Ticket 2 Ticket 3 Ticket 4 Ticket 5
If you add numbers to the ticket 1 and then the others it works... If you skip the ticket 1 and add numbers to the other ones, when you try to buy you get this error:
ContextErrorException: Notice: Undefined offset: 0 in C:\wamp\www\Digidis\front\src\MediaparkLt\UserBundle\Service\MoneyManager.php line 313
The full stack:
array('cartProduct' => array('title' => 'EUROMILLONES', 'price' => '2.35', 'product' => '2', 'ticket_id' => '1433921783_19792', 'numbers' => '8,13,14,17,37', 'stars' => '4,7', 'betslip' => '{"duration":"1","subscription":"false","jsPrice":"235","type":"simple","numbers1":"0,0,0,0,0","numbers2":"8,13,14,17,37","numbers3":"0,0,0,0,0","numbers4":"0,0,0,0,0","numbers5":"0,0,0,0,0","stars1":"0,0","stars2":"4,7","stars3":"0,0","stars4":"0,0","stars5":"0,0","dayOfWeek":"3"}', 'is_syndicate' => false, 'draw' => object(DateTime)), 'product' => object(Product), 'user' => object(User), 'reference' => null, 'paymentResult' => 'Authorised', 'bets' => object(stdClass), 'individualBets' => array(), 'tickets' => array(array('numbers' => '8,13,14,17,37', 'stars' => '4,7')), 'k' => '0', 't' => array('numbers' => '0,0,0,0,0', 'stars' => '0,0'), 'is_ticket_filled' => false, 'week_id' => array(array('ticketId' => '7005')), 'g' => '0', 'lastId' => '7005', 'purchase' => object(Purchase), 'price' => '2.35', 'bet' => object(Bet), 'euromillonesBet' => object(EuromillonesBet), 'drawDate' => array(object(DrawDate)), 'j' => '0')) in C:\wamp\www\Digidis\front\src\MediaparkLt\UserBundle\Service\MoneyManager.php line 313
As you can see first it gets the ticket 1, which is empty(or 0) and thats why it causes the error... How can I make it so that it skips the empty tickets?
Here is the controller where the error occurs:
$bets = json_decode($cartProduct['betslip']);
$individualBets = array();
$tickets = array(
array('numbers' => $bets->numbers1, 'stars' => $bets->stars1),
array('numbers' => $bets->numbers2, 'stars' => $bets->stars2),
array('numbers' => $bets->numbers3, 'stars' => $bets->stars3),
array('numbers' => $bets->numbers4, 'stars' => $bets->stars4),
array('numbers' => $bets->numbers5, 'stars' => $bets->stars5)
);
if ($bets->type === 'simple') {
foreach ($tickets as $k => $t) {
$is_ticket_filled = ((int) str_replace(',', '', $t['numbers'])) > 0;
if (!$is_ticket_filled) {
unset($tickets[$k]);
}
}
} else if ($bets->type === 'multiple') {
$tickets = array(array('numbers' => $bets->numbers1, 'stars' => $bets->stars1));
}
$week_id = null;
for ($k = 0; $k < (count($tickets)); $k++) {
for ($g = 0; $g < $bets->duration; $g++) {
if (!isset($week_id[$g])) {
$week_id[$g] = $this->entityManager->getRepository('MediaparkLtLotteryBundle:Bet')->getLastTicketId();
if ($week_id[$g]) {
$week_id[$g]['ticketId'] ++;
} else {
$week_id[$g]['ticketId'] = 0;
}
}
$lastId = $week_id[$g]['ticketId'];
$purchase = new Purchase();
$purchase->setUser($user);
$purchase->setDrawDate($cartProduct['draw']);
$purchase->setProduct($product);
$purchase->setReference($reference);
$price = $cartProduct['price'];
$bet = new Bet();
if ('eurojackpot' == $product->getAlias()) {
$euromillonesBet = new EurojackpotBet();
} else {
$euromillonesBet = new EuromillonesBet();
}
$drawDate = $this->entityManager->getRepository('MediaparkLtLotteryBundle:DrawDate')->findByDrawDate($cartProduct['draw']);
if (!$drawDate)
die('no draw date found ' . $cartProduct['draw']->format('Y-m-d H:i:s'));
$bet->setDrawDate($drawDate[0]);
$bet->setTicketId($lastId);
if (strtoupper($paymentResult) === 'AUTHORISED') {
$bet->setStatus(BetStatus::AUTHORISED);
} else {
$bet->setStatus(BetStatus::FAILED);
}
$bet->setWinnings(0);
$euromillonesBet->setBet($bet);
/// LINE 313 ABOVE!!!!!!!
$numbers = $this->getNumbersArray($tickets[$k]['numbers']);
$j = 0;
foreach ($numbers as $number) {
$j++;
$name = 'setN' . $j;
$euromillonesBet->$name($number);
}
$numbers = $this->getNumbersArray($tickets[$k]['stars']);
$euromillonesBet->setS1($numbers[0]);
$euromillonesBet->setS2($numbers[1]);
$euromillonesBet->setAmountOfStars(Bet::NUMBER_OF_STARS);
$purchase->addBet($bet);
$purchase->setPricePaid($price);
if (strtoupper($paymentResult) === 'AUTHORISED') {
$purchase->setStatus(PaymentStatus::AUTHORISED);
} else {
$purchase->setStatus(PaymentStatus::FAILED);
}
if ($bets->subscription === "true") {
$contract = new PurchaseContract();
$contract->setAccumulatedWinnings(0);
$contract->setCancellationDate(null);
$contract->setFirstDrawDate($purchase->getDrawDate());
$contract->setLastRenewedDate($purchase->getDrawDate());
$contract->setNextRenewalFirstDrawDate($purchase->getDrawDate());
// $contract->setPurchase($purchase);
$contract->setStatusPurchaseContract(1);
$contract->setWeeks(1);
$purchase->setPurchaseContract($contract);
$this->entityManager->persist($contract);
}
if ($g == 0)
$individualBets[] = $euromillonesBet;
$this->entityManager->persist($bet);
$this->entityManager->persist($euromillonesBet);
$this->entityManager->persist($purchase);
$this->entityManager->flush();
}
}
return $individualBets;
}
From what I see the bet type in your object is set to "type":"simple" and numbers1":"0,0,0,0,0"
$is_ticket_filled = ((int) str_replace(',', '', $t['numbers'])) > 0;
//(int) 00000 = 0
if (!$is_ticket_filled) {
unset($tickets[$k]);
}
Is causing the issue since unset does not reset the array indexes.
http://ideone.com/5q74Wv
Then later you iterate using for($k=0; $k < count($tickets); $k++)
You should instead rebase the array after using unset($tickets[$k])
if ($bets->type === 'simple') {
//...
$tickets = array_values($tickets);
}
or check the existence of the ticket when iterating over indexes
$ticketCount = count($tickets);
for ($k=0; $k < $ticketCount; $k++) {
if (false === isset($tickets[$k]) {
continue;
}
//...
}
or easier still, iterate over the existing tickets array using foreach instead of for.
foreach ($tickets as $k => $ticket) {
//...
}
Then change $tickets[$k] with just $ticket since $k is not used anywhere else.
I have the following code, and i keep getting undefined index error, the code is failing on test5() but i'm unable to find the error.
<?php
function test1() {
$vars = [0, 1, 2, 4, 3];
for ($i = 0; $i < count($vars); $i++) {
print $vars[$i] . "\n";
}
}
function test2() {
$flavors = ['vanilla', 'pistachio', 'banana', 'caramel', 'strawberry'];
$favorite = 'banana';
foreach ($flavors as $key => $flavor) {
if ($flavor === $favorite) {
print $key . "\n";
break;
}
}
}
function test3() {
$stuff = ['shoes', 33, null, false, true];
$selected = 0;
foreach ($stuff as $key => $thing) {
if ($thing == $selected) {
print $key . "\n";
break;
}
}
}
function test4() {
$four = 4;
$five = test4_helper($four);
print "four: $four\n";
print "five: $five\n";
}
function test4_helper(&$arg) {
$return = $arg++;
return $return;
}
function test5() {
$products = [
'Trek Fuel EX 8' => [
'price' => 2000,
'quantity' => 1
],
'Trek Remedy 9' => [
'price' => 2600,
'quantity' => 2
],
'Trek Scratch 8' => [
'price' => 3500,
'quantity' => 1
]
];
$total = 0;
$callback = function ($product, $name) {
//$total = 0;
$tax = 1.2;
$price = $product[$name]['price'];
$total += ($price * $product[$name]['quantity']) * $tax;
return $total;
};
array_walk($products, $callback);
print "$total\n";
}
/* * **********************************
* *** DO NOT EDIT BELOW THIS LINE ****
* *********************************** */
$tests = 5;
for ($i = 1; $i <= $tests; $i++) {
$function = "test$i";
print "\n\n==== Test $i ====\n";
$function();
print "==== END of test $i ====\n <br>";
}
what is the problem with this code?
it looks that it's failing on test 5
PHP closures are not like JavaScript ones in that they do not inherit the parent scope. You need to pass in any dependencies via the use construct. In your example...
$callback = function ($product, $name) use ($total) {
// etc
See http://php.net/manual/functions.anonymous.php#example-166
Arrays in PHP are defined like this:
$products = array(
'Trek Fuel EX 8' => array(
'price' => 2000,
'quantity' => 1
),
'Trek Remedy 9' => array(
'price' => 2600,
'quantity' => 2
),
'Trek Scratch 8' => array(
'price' => 3500,
'quantity' => 1
)
);
Which means you also need to look at $vars = [0, 1, 2, 4, 3]; and $flavors = ['vanilla', 'pistachio', 'banana', 'caramel', 'strawberry']; and fix them too.
I'm having some problems building an hierarchistic array structure - I have almost got it done, but there's something unexplained that makes my array look weird which I hoped you could help me with.
The tree structure is:
root
|data
|-children
|--data
|--children
|---data
|---children
Any child can have any number of children and each child can have any number of parents.
I have a function that builds the tree:
private function build_tree($rows,$parent) {
$i = 0;
$response -> result = array();
$result = array();
$leaf = false;
foreach($rows as $row) {
if($row['parent_id'] == $parent) {
$leaf = is_null($row['treeDef']) ? false : true;
$workingSet = array_merge($result,array(
'id' => (int)$i,
'parent_id' => (int)$row['parent_id'],
'child_id' => (int)$row['id'],
'name' => (string)$row['resourceName'],
'updated' => strtotime($row['updated']),
'purchasedUnit' => (string)$row['purchasingUnit'],
'purchasedCost' => (double)$row['purchasingCosts'],
'purchasedDiscount' => (double)$row['discount'],
'estimateUnit' => (string)$row['estimatingUnit'],
'profitAddOn' => (string)$row['profitAddOn'],
'landedCost' => (double)$row['landedCost'],
'unitCorrelation' => (double)$row['unitCorrelation'],
'leadTime' => (string)$row['leadTime'],
'ResourceClassShortname' => (string)$row['ResourceClassShortname'],
'supplierName' => (string)$row['nameSupplier'],
'iconCls' => (string)$row['typeIcon'],
'leaf' => $leaf
));
$hasChildren = $this->Resource_model->has_children($rows,$row['id']);
if ($hasChildren->check) {
if (!$leaf) {
for($j=0; $j <= ($hasChildren -> rows); $j++) {
$parentArray = $workingSet;
$childArray = $this -> Resource_model -> build_tree($rows,$row['id']);
$workingSet = array_merge($parentArray,array('children' => $childArray -> result));
}
}
}
$result[$i] = $workingSet;
$i++;
}
}
$response -> result = $result;
$response -> rows = $i;
return $response;
}
Which produces this JSON:
Big picture
Every item that has 2 children (or more? - no test values) gets the first item like it should be, but the second item contains the first item as well - duplicating all the results.
Any help appreciated.
Instead of array_merge use array_push - this will add the children subarray instead of trying merging it with the one already existing...
It's in this part of code (already edited):
$hasChildren = $this->Resource_model->has_children($rows,$row['id']);
if ($hasChildren->check) {
if (!$leaf) {
for($j=0; $j <= ($hasChildren->rows); $j++) {
$parentArray = $workingSet;
$childArray = $this->Resource_model->build_tree($rows,$row['id']);
$workingSet = array_push($parentArray,array('children' => $childArray->result));
}
}
}
Made it work, here's the final code:
private function build_tree($rows,$parent) {
$i = 0;
$response -> result = array();
$result = array();
$leaf = false;
$newArray = array();
foreach($rows as $row) {
if($row['parent_id'] == $parent) {
$leaf = is_null($row['treeDef']) ? false : true;
$newArray = array(
'id' => (int)$i,
'parent_id' => (int)$row['parent_id'],
'child_id' => (int)$row['id'],
'name' => (string)$row['resourceName'],
'updated' => strtotime($row['updated']),
'purchasedUnit' => (string)$row['purchasingUnit'],
'purchasedCost' => (double)$row['purchasingCosts'],
'purchasedDiscount' => (double)$row['discount'],
'estimateUnit' => (string)$row['estimatingUnit'],
'profitAddOn' => (string)$row['profitAddOn'],
'landedCost' => (double)$row['landedCost'],
'unitCorrelation' => (double)$row['unitCorrelation'],
'leadTime' => (string)$row['leadTime'],
'ResourceClassShortname' => (string)$row['ResourceClassShortname'],
'supplierName' => (string)$row['nameSupplier'],
'iconCls' => (string)$row['typeIcon'],
'leaf' => $leaf
);
$hasChildren = $this -> Resource_model -> has_children($rows,$row['id']);
if ($hasChildren->check) {
for($j=0; $j <= ($hasChildren -> rows); $j++) {
$childArray = $this -> Resource_model -> build_tree($rows,$row['id']);
$newArray = array_merge($newArray, array('children' => $childArray -> result));
}
}
$result[$i] = $newArray;
$i++;
}
}
$response -> result = $result;
$response -> rows = $i;
return $response;
}