PHP & Array: count distance between two keys - php

Having an array similar to the one below:
$steps = array(0 => 'aaa', 1 => 'bbb', 2 => 'ccc', ......, 7 => 'hhh', 8 => 'iii', .....);
How can I calculate how many steps (key) do I need to reach key 2 from key 7 respecting the sequence?

If you have numeric keys that never have any missing numbers, you can use basic subtraction.
If you need to account for possible missing numbers, or the keys are not numeric, you can use a combination of array_keys() and array_search():
$array = array(
0 => 'aaa',
1 => 'bbb',
3 => 'ccc',
'four' => 'ddd',
900 => 'eee',
13 => 'fff'
);
$from = 1;
$to = 900;
$keys = array_keys($array);
$from_index = array_search($from, $keys); // 1
$to_index = array_search($to, $keys); // 4
$steps = $to_index - $from_index;
// 3 steps: from 1-3, from 3-'four' and from 'four'-900

I solved this problem by writing this code:
$tot_step = 0;
$steps = array(
0 => 'aaa',
1 => 'bbb',
2 => 'ccc',
3 => 'ddd',
4 => 'eee',
5 => 'fff',
6 => 'ggg',
7 => 'hhh',
8 => 'iii',
9 => 'jjj',
10 => 'aaa'
);
$from = "ddd";
$to = "bbb";
$from_index = array_search($from, $steps);
$to_index = array_search($to, $steps);
$last = $steps[(count($steps)-1)];
if ($from_index > 0) {
if ($to == $last)
$to_index = (count($steps)-1);
$arr_l = count($steps);
$mila = array();
for ($ind = $from_index; $ind <= ($arr_l-1); $ind++) {
if ($to == $last) {
if ($steps[$ind] != $last)
$mila[] = $steps[$ind];
} else {
$mila[] = $steps[$ind];
}
unset($steps[$ind]);
}
if (!empty($mila)) {
for ($i = (count($mila)-1); $i >= 0; $i--)
array_unshift($steps, $mila[$i]);
}
$to_new = array_search($to, $steps);
foreach ($steps as $key => $value) {
if ($key == $to_new)
break;
else
$tot_step++;
}
} elseif ($from_index == 0) {
if ($to_index == $from_index) {
$tot_step = (count($steps)-1);
} else {
foreach ($steps as $key => $value) {
if ($key == $to_index)
break;
else
$tot_step++;
}
}
}
echo $tot_step;
I hope it will be useful to someone

Related

How to get array values certain condition

There is something I want to ask to take the array value based on the biggest score in score keys and the smallest strlen in word keys, this sample array:
<?php
$data = array(
'0' => array('score' => '4','word' => 'titiek'),
'1' => array('score' => '4','word' => 'titik'),
'2' => array('score' => '4','word' => 'titie'),
'3' => array('score' => '3','word' => 'tuatuka'),
'4' => array('score' => '3','word' => 'titiks'),
);
$result = //do something??
print_r($result);
?>
let we see in $data array, condition the biggest score and the smallest strlen we have $data[1] and $data[2], right? but, I need the first queue. the result print_r($result) is a:
Array ( [score] => 4 [word] => titik )
You can use usort to sort your array by score and then word length, and then your desired result will be in $data[0]. Note that we rank equal word lengths higher so that we return the first one of the shortest length that we see in the array.
usort($data, function($a, $b) {
if ($a['score'] == $b['score'])
return (strlen($a['word']) >= strlen($b['word'])) ? 1 : -1;
else
return $b['score'] - $a['score'];
});
print_r($data[0]);
An alternate method (not relying on usort behaviour) is to find the maximum score and then process the array looking for the first, shortest string with that score:
$max_score = max(array_column($data, 'score'));
$minlength = PHP_INT_MAX;
foreach ($data as $key => $value) {
if ($value['score'] != $max_score) continue;
if (strlen($value['word']) < $minlength) {
$minlength = strlen($value['word']);
$index = $key;
}
}
print_r($data[$index]);
Output (same for both):
Array (
[score] => 4
[word] => titik
)
Demo on 3v4l.org
You can use array_reduce and by using ">=" "<=" it will keep the previous value
$result = array_reduce($data, function ($previous, $current) {
return $previous['score'] >= $current['score'] && strlen($previous['word']) <= strlen($current['word']) ? $previous : $current;
});
var_dump($result);
Here is how you can find it:
$data = array(
'0' => array('score' => '4','word' => 'titiek'),
'1' => array('score' => '4','word' => 'titik'),
'2' => array('score' => '4','word' => 'titie'),
'3' => array('score' => '3','word' => 'tuatuka'),
'4' => array('score' => '3','word' => 'titiks'),
);
$maxScore = 0; $minLen = 0;
foreach($data as $key => $arr) {
$score = $arr['score'];
$len = strlen($arr['word']);
if($score > $maxScore) {
$maxKey = $key;
$minLen = $len;
$maxScore = $score;
} else if ($score == $maxScore && $len < $minLen) {
$maxKey = $key;
$minLen = $len;
}
}
var_dump($data[$maxKey]);
I have used var_dump to show the result. $data[$maxKey] is the required result.

Two Array Queue Algorithm in php

I have two array, and want to have queueing system in third array in php
Array 1
3
4
5
6
7
Array 2
24 => U //Unavailable
39 => A //Available
55 => A //Available
77 => A //Available
Result expected:-
So in Array 3 I want the one's that are Available
Array 3
39 => 3
55 => 4
77 => 5
Also the data from Array 1 should get deleted once it is assigned to Array 3.
Array 1 should become
6
7
Let me know. Thanks in advance.
I would start with the numbers from $array2 which are available.
$availables = [];
foreach ($array2 as $key => $value) {
if ($value == 'A') $availables[] = $key;
}
Now we have two arrays: $availables and $array1 one, and we need to combine them where one represents the keys and the other the values of the new array. You would do this with this function:
http://nl1.php.net/manual/en/function.array-combine.php
The only thing we need to be careful about is the size of the arrays, so:
$size1 = count($array1);
$size2 = count($availables);
if ($size1 > $size2) $array1 = array_slice($array1,0,$size2);
if ($size2 > $size1) $availables = array_slice($availables,0,$size1);
and then we can combine them:
$array3 = array_combine($availables,$array1);
See: https://eval.in/1056040
It would be better to put this in a function or a method, so that changes in the arrays don't affect the original arrays.
There are, of course, lot's of other ways to do the last two steps, for instance:
$array3 = [];
foreach ($array1 as $key => $number)
{
if (!isset($availables[$key])) break;
$array3[$availables[$key]] = $number;
}
See: https://eval.in/1056044
foreach($array[1] as $key => $value){
if($value === 'A') $array[2][$key] = $value;
}
I would do something like this:
<?php
$quee = range(1, 100);
$stations = ['1' => ['status' => 'A', 'client' => ''], '2' => ['status' => 'A', 'client' => ''], '3' => ['status' => 'U', 'client' => null], '4' => ['status' => 'A', 'client' => '']];
while ($quee) {
foreach ($stations as $name => &$station) {
if ($station['status'] === 'U') {
echo 'Station ' . $name . ' is busy' . PHP_EOL;
$station['status'] = 'A';
} else {
$station['status'] = 'U';
$client = array_shift($quee);
if ($client != null) {
$station['client'] = $client;
echo 'Im doing client ' . $client . ' on station ' . $name . PHP_EOL;
} else {
break;
}
}
}
}

Conditional remove adjacent duplicates from array

I have following code that removes adjacent duplicates from the $myArray
<?php
$myArray = array(
0 => 0,
1 => 0,
2 => 1,
5 => 1,
6 => 2,
7 => 2,
8 => 2,
9 => 0,
10 => 0,
);
$previtem= NULL;
$newArray = array_filter(
$myArray,
function ($currentItem) use (&$previtem) {
$p = $previtem;
$previtem= $currentItem;
return $currentItem!== $p ;
}
);
echo "<pre>";
print_r($newArray);
?>
It works perfectly fine, but I have to change a condition bit for value 2. That means for other values we can pick first occurrence and ignore the others. But for 2, we need to pick last occurrence and ignore others.
So required output is
Array
(
[0] => 0 //first occurrence of 0 in $myArray
[2] => 1 //first occurrence of 1 in $myArray
[8] => 2 //last occurrence of 2 in the $myArray
[9] => 0 //first occurrence of 0 in $myArray
)
How to modify my code to achieve above result??
In reality I have multidimensional array, but for better explanation I have used single dimensional array here in the question.
UPDATE
My actual array is
$myArray = array(
0 => array("Value"=>0, "Tax" => "11.00"),
1 => array("Value"=>0, "Tax" => "12.00"),
2 => array("Value"=>1, "Tax" => "13.00"),
5 => array("Value"=>1, "Tax" => "14.00"),
6 => array("Value"=>2, "Tax" => "15.00"),
7 => array("Value"=>2, "Tax" => "16.00"),
8 => array("Value"=>2, "Tax" => "17.00"),
9 => array("Value"=>0, "Tax" => "18.00"),
10 => array("Value"=>0, "Tax" => "19.00"),
);
And my actual code
$previtem= NULL;
$newArray = array_filter(
$myArray,
function ($currentItem) use (&$previtem) {
$p["Value"] = $previtem["Value"];
$previtem["Value"] = $currentItem["Value"];
return $currentItem["Value"]!== $p["Value"] ;
}
);
Thanks
This should do what you are looking for.
function array_filter($a) {
$na = array();
$first = true;
$p = null;
$wantlast = false;
foreach ($a as $v) {
if ($wantlast) {
($v != $p) ? $na[] = $p: null;
}
$wantlast = ($v == 2) ? true : false;
if (!$wantlast) {
(($v != $p) || ($first))? $na[] = $v : null;
}
$p = $v;
$first = false;
}
return $na;
}
$myArray = array(
0 => 0,
1 => 0,
2 => 1,
5 => 1,
6 => 2,
7 => 2,
8 => 2,
9 => 0,
10 => 0,
);
$previtem= NULL;
$newArray = array_filter(
$myArray,
function ($currentItem, $key) use (&$previtem,$myArray) {
$p = $previtem;
if($currentItem != 2){
$previtem = $currentItem;
}else{
$lastkey = array_search(2,(array_reverse($myArray, true)));
if($key != $lastkey)
$currentItem = $previtem;
}
return $currentItem!== $p ;
}, ARRAY_FILTER_USE_BOTH
);
echo "<pre>";
print_r($newArray);

Symfony2 undefined offset: 0 error with lottery project

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.

Accurately setting up a ranking from an single dimension array

I would like to display some array data in relation to a competition, each player is ranked by the number of points they have, however if a player has the same amount of points to somebody else they should both have the exact same ranking position.
For instance...
1st Bob 500pts
2nd Joe 350pts
3rd Tom 250pts
3rd Tim 250pts
5th Jay 100pts
In this instance, as Tom & Tim have the exact same number of points they should be joint third, making the next person down 5th (rather than 4th), can anyone suggest the best way to achieve this with a simple array similar to follows
array('Bob' => 500, 'Joe' => '350', 'Tom' => '250', 'Tim' => '250', 'Jay' => '100');
Can anyone suggest the 'cleanest' solution for achieving this
This code will work for you:
$array = array('Bob' => 500, 'Joe' => '350', 'Tom' => '250', 'Tim' => '250', 'Jay' => '100');
arsort($array, SORT_NUMERIC);
$previousPoints = null;
$position = $total = 1;
foreach($array as $name=>$points) {
if ($points != $previousPoints) {
$position = $total;
}
echo $position.' '.$name.' '.$points."\n";
$previousPoints = $points;
$total++;
}
Online demo here.
$ar = array(
'Bob' => 500,
'Tim' => '250',
'Joe' => '350',
'Tom' => '250',
'Jay' => '100'
);
arsort($ar, SORT_NUMERIC);
$i = 1;
$previous = 1;
$previousPosition = 1;
foreach($ar as $k => $v)
{
if($v === $previous)
{
//If the value now is the same as the previous value use the previous position
echo "Position: $previousPosition, $k : $v <br />";
}
else
{
echo "Position: $i, $k : $v <br />";
}
//Previous value
$previous = $v;
//Previous Position
$previousPosition = $i;
//Always increment the value
$i++;
}
Try below code:
$a = array('Bob' => 500, 'Joe' => '350', 'Tom' => '250', 'Tim' => '250', 'Jay' => '100');
arsort($a);
$rank = 1;
$index = 1;
$prevUserPoints = 0;
foreach($a as $name=>$points) {
if($points != $prevUserPoints) {
$rank = $index;
}
$index++;
echo $rank . ' ' . $name . ' ' . $points . "\n";
$prevUserPoints = $points;
}
For displaying 1 as 1st, 2 as 2nd etc, you can use something like below:
function ordSuffix($n) {
$str = "$n";
$t = $n > 9 ? substr($str,-2,1) : 0;
$u = substr($str,-1);
if ($t==1) return $str . 'th';
else switch ($u) {
case 1: return $str . 'st';
case 2: return $str . 'nd';
case 3: return $str . 'rd';
default: return $str . 'th';
}
}
example: echo ordSuffix(23); This prints 23rd
Just a basic and alternative point of view you might want to consider.
$arr = [
"Joe" => "350",
"Tom" => "250",
"Jay" => "200",
"Tim" => "250",
"Bob" => "500",
"John" => "250" ,
"Paul" => "251.40"
];
$rank = array();
array_walk($arr,function($v, $k) use (&$rank)
{
if(isset($rank[$v]))
{
$rank[$v][$k] = $v;
asort($rank[$v]); //alphabetical order John, Tim, Tom
} else
{
$rank[$v] = array($k => $v);
}
});
krsort($rank);
var_dump(array_values($rank));

Categories