I need help, example, i got array with 4 for team names there.
array('logiX.L4d22','Lust','Marat and Friends','Pandas of Belgium');
I want to make 3 dimensional array where first is a round, second is a match, and the third is teams who plays with eachother, there will be always 2 teams.
the logic must make that all teams must be played with all others and in one round, any team will play only one match, so if we got 5 teams then in some round one team must just wait to the next round.
it must produce something like this:
array
0 =>
array
0 =>
array
0 => string 'logiX.L4D2' (length=10)
1 => string 'Lust' (length=4)
1 =>
array
0 => string 'Marat and Friends' (length=17)
1 => string 'Pandas of Belgium' (length=17)
1 =>
array
0 =>
array
0 => string 'logiX.L4D2' (length=10)
1 => string 'Marat and Friends' (length=17)
1 =>
array
0 => string 'Lust' (length=4)
1 => string 'Pandas of Belgium' (length=17)
2 =>
array
0 =>
array
0 => string 'logiX.L4D2' (length=10)
1 => string 'Pandas of Belgium' (length=17)
1 =>
array
0 => string 'Lust' (length=4)
1 => string 'Marat and Friends' (length=17)
It must be work with 2,3,5 ...10 ...12 teams.
I hope that you can help me out, i already spent 1 and half days for that.
Some Googling on Round Robin algorithm PHP gives the following:
http://speedtech.it/blog/2009/03/15/round-robin-algorithm-php/
http://www.phpbuilder.com/board/showthread.php?t=10300945
I hope you'll find what you're looking for.
EDIT
Adding my attempt for this, following the round-robin algorithm described on Wikipedia.
If teams number is odd, it adds a team in the array (null value), so you can retrieve the "waiting team" for each round.
<?php
$teams = range('a', 'g');
function make_rounds($teams)
{
$nb_teams = count($teams);
if ($nb_teams % 2 != 0)
{
$teams[] = null;
$nb_teams++;
}
$nb_rounds = $nb_teams - 1;
$nb_matches = $nb_teams / 2;
$rounds = array();
for($round_index = 0; $round_index < $nb_rounds; $round_index++)
{
$matches = array();
for($match_index = 0; $match_index < $nb_matches; $match_index++)
{
if ($match_index == 0)
$first_team = $teams[0];
else
$first_team = $teams[(($nb_teams-2) + $match_index - $round_index) % ($nb_teams-1) + 1];
$second_team = $teams[(($nb_teams*2) - $match_index - $round_index - 3) % ($nb_teams-1) + 1];
$matches[] = array($first_team, $second_team);
}
$rounds[] = $matches;
}
return $rounds;
}
print_r(make_rounds($teams));
My version of solution. I would call it brute force. However, it works somehow. (Or it looks like that.)
<?php
$a = array('a','b','c','d','e','f','g');
function do_array($a)
{
$lim = sizeof($a) - 1;
# Create an array of all matches to play.
$cross = array(array());
foreach (range(0,$lim) as $k_row):
foreach (range(0,$lim) as $k_col):
if ($k_row >= $k_col):
$toput = false;
else:
$toput = array($a[$k_row],$a[$k_col]);
endif;
$cross[$k_row][$k_col] = $toput;
endforeach;
endforeach;
$ret = array();
foreach (range(0,$lim) as $k_round):
$round = array();
# $tmp array holds all possible matches
# to play in current round.
$tmp = $cross;
$i = 0;
foreach (range(0,$lim) as $k_row):
foreach (range(0,$lim) as $k_col):
if ($math = $tmp[$k_row][$k_col]):
$cross[$k_row][$k_col] = false;
# These matches are not possible
# in the current round.
foreach (range(0,$lim) as $k):
$tmp[$k][$k_col] = false;
$tmp[$k][$k_row] = false;
$tmp[$k_col][$k] = false;
$tmp[$k_row][$k] = false;
endforeach;
$round[] = $math;
endif;
endforeach;
endforeach;
if ($round):
$ret[] = $round;
endif;
endforeach;
return $ret;
}
print_r (do_array($a));
?>
Related
Rearrange words in Array based on position of the first array. In my code there are two array my first array is the base array from which i am going to compare it with second array and make the position same as first array.
Consider 2 input
By considering 1 input as base i am applying levenshtein(metaphone(each word database),metaphone(each word of bank)) then based on that arranging the words of bankdata in new array
databaseName = LAL BAHADUR SHASTRI
bankdata = SHASTRI LAL
source code will only rearrange bankdata and stored in in new array
current output of bankdata : LAL SHASTRI
Rearrangement is happening properly just need to arrange words in array
$db = 'LAL BAHADUR SHASTRI YADAV';
$bank = 'SHASTRI LAL';
$a = reArrangeArray($db,$bank);
function reArrangeArray($db,$bank)
{
$dataBaseName = $db;
$bankdataRows = [$db,$bank,];
$dbWords = preg_split("#[\s]+#", $dataBaseName);
foreach ($bankdataRows as $bankdata)
{
$bankWords = preg_split("#[\s]+#", trim($bankdata));
$result = [];
if(!empty($bankWords))
foreach ($dbWords as $dbWord)
{
$idx = null;
$least = PHP_INT_MAX;
foreach ($bankWords as $k => $bankWord)
if (($lv = levenshtein(metaphone($bankWord),metaphone($dbWord))) < $least)
{
$least = $lv;
$idx = $k;
}
#$result[] = $bankWords[$idx];
unset($bankWords[$idx]);
}
$result = array_merge($result, $bankWords);
var_dump($result);
}
}
Case 1:
CURRENT OUTPUT
array (size=4)
0 => string 'LAL' (length=3)
1 => string 'BAHADUR' (length=7)
2 => string 'SHASTRI' (length=7)
3 => string 'YADAV' (length=5)
array (size=4)
0 => string 'LAL' (length=3)
1 => string 'SHASTRI' (length=7)
2 => null
3 => null
Expected Output
I need array position as same as databaseArray
$dbName = 'LAL BAHADUR SHASTRI YADAV';
$bankName = 'SHASTRI LAL';
array of db (size=4)
0 => string 'LAL' (length=3)
1 => string 'BAHADUR' (length=7)
2 => string 'SHASTRI' (length=7)
3 => string 'YADAV' (length=5)
array of bankname (size=4)
0 => string 'LAL' (length=3)
1 => #
2 => string 'SHASTRI' (length=7)
3 => ###
if word not found in first array it should be place with # since position is 3 which dont have matching element it has 3 #
Case 2
Input :
$dbName = NikithaRani MohanRao
$bankdata = Nikitha Rani Mohan Rao
Output :
$newbankdata = NikithaRani MohanRao
It should concatenate the word if found concatenated in $dbName
<?php
$DatabaseName = "LAL BAHADUR SHASTRI YADAV";
$BankName = "SHASTRI LAL";
$data = explode(" ", $DatabaseName);
$bank = explode(" ", $BankName);
sort($data);
sort($bank);
if($data == $bank){
echo "true";
exit;
}
$lev = 0;
foreach($data as $key => $name){
$lev += levenshtein($name, $bank[$key]);
}
echo PHP_EOL . $lev;
?>
I have a problem getting the min and max values from a row of numbers with a special difference of 1800 in it. For better understanding, I want to give you the following example:
array(0,1800,3600,5400,7200,12600,14400,16200,23400,25200);
The special difference for my row of numbers is 1800. When a number has an exactly difference of 1800 to the next number, they are numbers of the same row. If not, they are numbers of another row. So I need a function which will produce the following output for the array mentioned above:
Output 1: min value 0, max value 7200
Output 2: min value 12600, max value 16200
Output 3: min value 23400, max value 25200
I hope you understand my question. Sorry for my english and thanks in advance.
It's alway better to show some of the code you have tried so far.
However here is a very short code example I put together.
$lists = [];
$special = 1800;
$array = [0, 1800, 3600, 5400, 7200, 12600, 14400, 16200, 23400, 25200];
$currentList = [];
foreach ($array as $number) {
if (empty($currentList)) {
$currentList[] = $number;
} else {
$last =(end($currentList) + $special);
if ($number === $last) {
$currentList[] = $number;
} else {
$lists[] = $currentList;
$currentList = [$number];
}
}
}
$lists[] = $currentList;
var_dump($lists);
This will output the following array, which could be transformed in the output you want.
array (size=3) 0 =>
array (size=5)
0 => int 0
1 => int 1800
2 => int 3600
3 => int 5400
4 => int 7200 1 =>
array (size=3)
0 => int 12600
1 => int 14400
2 => int 16200 2 =>
array (size=2)
0 => int 23400
1 => int 25200
You can create a helper function to iterate your input and add the values to an array of rows
function getRows($input, $specialDifference) {
$rows = array();
$newRow = true;
for ($index = 0; $index < count($input); $index++) {
if ($newRow) {
$rows[] = array();
$newRow = false;
}
$rows[count($rows) - 1][]= $input[$index];
$newRow = ((count($input) > $index + 1) && ($input[$index + 1] - $input[$index] !== $specialDifference));
}
return $rows;
}
I have a multidimensional array in PHP, where the outer array contains several thousands items and each item inside is an array itself with the values "key1", "key2" and "count":
myExistingArray (size=99999 VERY BIG)
public 0 =>
array (size=3)
'key1' => string '15504'
'key2' => string '20'
'count' => string '1'
public 1 =>
array (size=3)
'key1' => string '15508' (length=5)
'key2' => string '20' (length=2)
'count' => string '2' (length=1)
public 2 =>
array (size=3)
'key1' => string '15510' (length=5)
'key2' => string '20' (length=2)
'count' => string '5' (length=1)
....many more similar items
I want to transform this into a very simple array, where the former values from "key1" and "key" are concatenated to be a new key that points to the corressponding "count" value like so:
myNewArray (size=99999 VERY BIG)
<key1>_<key2> => <count>
15504_20 => string '1' (length=1)
15508_20 => string '2' (length=1)
15510_20 => string '5' (length=1)
Performance is very important for me since the outer array has several thousand items. Is there a fast method in PHP? The only thing I got was a simple iteration, but this seems to slow for me:
// works but I am looking for a faster version
$myNewArray = array();
foreach ($myExistingArray as $item) {
$myNewArray [$item["key1"]."_".$item["key1"]]=$item["count"];
}
EDIT / Underlying problem
Some people rightfully added that my current solution is already in O(n) and mentioned that there is no built-in function in PHP to speed this up.
I get "myExistingArray" from a mysql database query. I basically have job objects and want to group them by their status and their event_id. The query similiar to this:
select count(job.id) as count, job.status as key1, job.event_id as key2
from job
group by job.status, job.event_id
I want to rearrange the keys so that later I can easily access the count of jobs for a certain event with a certain status.
Ordinarily, you'd be looking for either the array_walk or maybe the array_map function to transform arrays in PHP, but unfortunately neither of them can alter the key of the array that you want to transform. array_walk will preserve the keys, but won't alter them. So sadly, no, there's no built in function to do what you're asking.
Done a few test with the following results (almost all the same).
Test 1: [0.25861501693726]
Test 2: [0.20804476737976]
Test 3: [0.21039199829102]
Oldskool:[0.26545000076294]
Test 4: [0.35072898864746]
Doing a var_dump() on the merged array will slow things down (as expected), but if you keep it memory the data is not too bad to work with.
And the PHP used to test:
// Construct the raw data
$i = 0;
do {
$raw[] = array('key1' => mt_rand(10000,99999), 'key2' => mt_rand(10,99), 'count' => $i);
} while(++$i < 100000);
// Test 1
$before = microtime(true);
foreach($raw as $k => $v) {
$clean[$v['key1'].'_'.$v['key2']] = $v['count'];
}
$after = microtime(true);
echo 'Test 1:['.($after - $before).']<br />';
$clean = false;
$i = 0;
// Test 2
$before = microtime(true);
$max = count($raw);
do {
$clean[$raw[$i]['key1'].'_'.$raw[$i]['key2']] = $raw[$i]['count'];
} while(++$i < $max);
$after = microtime(true);
echo 'Test 2:['.($after - $before).']<br />';
$clean = false;
$i = 0;
// Test 3
$before = microtime(true);
$max = count($raw);
for($i; $i < $max; $i++) {
$clean[$raw[$i]['key1'].'_'.$raw[$i]['key2']] = $raw[$i]['count'];
}
$after = microtime(true);
echo 'Test 3:['.($after - $before).']<br />';
$clean = false;
// Test of Oldskool's suggestion
$before = microtime(true);
foreach (array_keys($raw) as $item) {
$clean[$raw[$item]['key1'].'_'.$raw[$item]['key2']] = $raw[$item]['count'];
}
$after = microtime(true);
echo 'Test Oldskool:['.($after - $before).']<br />';
$clean = false;
$i = 0;
// Test 4, just for fun
$before = microtime(true);
$max = count($raw);
do {
$c = array_pop($raw[$i]);
$clean[join('_', $raw[$i])] = $c;
} while(++$i < $max);
$after = microtime(true);
echo 'Test 4:['.($after - $before).']<br />';
Edit: Added a test for Oldskool example.
You could change your foreach to only iterate over the keys and not the entire sub-arrays, by changing it to:
foreach (array_keys($myExistingArray) as $item) {
$myNewArray[$myExistingArray[$item]['key1'] . '_' . $myExistingArray[$item]['key2']] = $myExistingArray[$item]['count'];
}
This will gain you some slight speed advantage (see comparison of the times here (array_keys method) and here (your original method)). On very large arrays, the difference will likely become more noticable.
If speed is the issue, and you are not using the final array as a map, I would create a generator, so that you don't have to precalculate everything.
$myExistingArray = [ ... ];
class MyNewArrayIterator implements IteratorAggregate {
protected $array;
public function __construct(array $array) {
$this->array = $array;
}
public function getIterator() {
foreach ($this->array as $value) {
yield $value['key1'] . '_' . $value['key2'] => $value['count'];
}
}
}
And then you can do:
$myNewArray = new MyNewArrayIterator($myExistingArray);
foreach($myNewArray as $key => $value) {
echo $key . ": " . $value;
}
This may or may not be useful in your use case.
I have array like this
$non_unique_zip
[0]->[0]91390
[1]ca
[2]1
[1]->[0]91391
[1]ca
[2]1
[2]->[0]91392
[1]ca
[2]1
[3]->[0]91390
[1]ca
[2]2
[4]->[0]91394
[1]ca
[2]2
so basically array has elements where arra[n][0] is zipcode and array[n][2] is buyer_id.
now out of this, i just want the zipcodes which have multiple buyers. so the only thing I want to extract is
[0]->[0]91390
[1]ca
since 91390 is the only zipcode which has buyer as 1 and 2.
I tried
$result = array();
$first = $non_unique_zip[0];
for($i=0; $i<count($non_unique_zip); $i++){
$result = array_intersect ($first, $non_unique_zip[$i]);
$first = $result;
}
but it just gives error undefined offset.
Any help will be appreciated.
If you call $records your starting array, here is a way to get the zips with 3 lines of code:
//array whose keys are zips, and values are # of occurances
$zips = array_count_values(array_column($records,0));
//filter keeps only zips which occur more than once.
$zips = array_filter($zips,function($n){return $n>1;});
//if you only need the zips, you're done! they are the keys
$zips = array_keys($zips);
Live demo
Use an array that keeps track of the zip codes that have already been encountered before. Then when you get a zip that's in that array, you know it's a duplicate.
$zips = array();
$result = array();
foreach ($non_unique_zip as $e) {
$code = $e[0];
if (isset($zips[$code])) { // duplicate, so add to $result
$result[$code] = array($code, $e[1]);
} else {
$zips[$code] = true; // first time, add it to $zips
}
}
Use array_walk like so:
<?php
$non_unique_zip = [
[91390, "ca"],
[91391, "ca"],
[91392, "ca"],
[91390, "ca"],
[91394, "ca"],
];
$unique_zip = [];
$duplicates = [];
array_walk($non_unique_zip, function($data) use(&$unique_zip, &$duplicates){
if(!in_array($data, $unique_zip)){
$unique_zip[] = $data;
}else{
$duplicates[] = $data;
}
});
var_dump( $duplicates );
// PRODUCES::
array (size=1)
0 =>
array (size=2)
0 => int 91390
1 => string 'ca' (length=2)
var_dump( $unique_zip );
// PRODUCES::
array (size=4)
0 =>
array (size=2)
0 => int 91390
1 => string 'ca' (length=2)
1 =>
array (size=2)
0 => int 91391
1 => string 'ca' (length=2)
2 =>
array (size=2)
0 => int 91392
1 => string 'ca' (length=2)
3 =>
array (size=2)
0 => int 91394
1 => string 'ca' (length=2)
I have a function that gets filenames from a directory, and then puts them in an array. All the filenames start with a code, as an example: -0-filename.php, -1-filename.php, -2-filename.php and -10-filename.php.
After some steps, the filenames are echoed out. The process looks like this:
rsort( $archiveArray );
$amount = count( $archiveArray );
$i = 0;
while( $i <= $amount )
{
echo $archiveArray[$i];
$i++;
}
Anyways. The problem is; when I get 10 files in the directory, and try to do the echoing process above, I get the names in a wrong order. - It's supposed to be :
-10-filename.php
-9-filename.php
-8-filename.php
...
-1-filename.php
-0-filename.php
But instead, I get
-9-filename.php
-8-filename.php
...
-10-filename.php
-1-filename.php
-0-filename.php
What's the quickest and easiest way to fix this?
EDIT:
If it wasn't obvious, the filenames are not always identical, even when not including the codes. The are always in this format: -number-randomtext.php, where number is always one higher than the last one, and randomtext can really be anything.
rsort has an optional second parameter called sort_flags. use SORT_NATURAL to do a "natural sort".
rsort($archiveArray, SORT_NATURAL);
Using rsort with SORT_NATURAL works if you're running PHP 5.4. If you're not, however:
natsort( $archiveArray );
$archiveArray = array_reverse( $archiveArray );
...will do the same thing. Just use natsort to do the natural order sorting, then reverse the array.
Test code (PHP 5.3.3):
php > $array = array( '-1-blah', '-2-foo', '-12-boo', '-11-yaay', '-3-bar' );
php > natsort( $array );
php > print_r( $array );
Array
(
[0] => -1-blah
[1] => -2-foo
[4] => -3-bar
[3] => -11-yaay
[2] => -12-boo
)
php > $array = array_reverse( $array );
php > print_r( $array );
Array
(
[0] => -12-boo
[1] => -11-yaay
[2] => -3-bar
[3] => -2-foo
[4] => -1-blah
)
php >
Another way to do this is usort, and write your own sorting criteria.
I used regular expressions to parse the numeric value from the string, as follows:
$files = array(
'-0-filename.php',
'-1-filename.php',
'-2-filename.php',
'-3-filename.php',
'-4-filename.php',
'-5-filename.php',
'-6-filename.php',
'-7-filename.php',
'-8-filename.php',
'-9-filename.php',
'-10-filename.php',
'-11-filename.php',
);
usort($files, 'CustomFileSequence');
var_dump($files);
function CustomFileSequence($a, $b)
{
$pattern = '/^\\-([0-9]*)\\-/';
preg_match($pattern, $a, $matches);
$a_val = $matches[1];
preg_match($pattern, $b, $matches);
$b_val = $matches[1];
if ($a_val < $b_val)
return 1;
if ($a_val > $b_val)
return -1;
if ($a_val == $b_val)
return 0;
}
Output is:
array
0 => string '-11-filename.php' (length=16)
1 => string '-10-filename.php' (length=16)
2 => string '-9-filename.php' (length=15)
3 => string '-8-filename.php' (length=15)
4 => string '-7-filename.php' (length=15)
5 => string '-6-filename.php' (length=15)
6 => string '-5-filename.php' (length=15)
7 => string '-4-filename.php' (length=15)
8 => string '-3-filename.php' (length=15)
9 => string '-2-filename.php' (length=15)
10 => string '-1-filename.php' (length=15)
11 => string '-0-filename.php' (length=15)
You need to convert the number to an int value. Right now it's being interpreted as a string. You can just do something like this:
$names = // your array of file names.
$sorted = // the sorted version names.
for($i = 0; $i < count($arr); $i++) {
$sorted[substr($names[$i], 1, strpos($names[$i], '-', 2))] = $names[$i];
}
print_r($sorted);
Basically, you take the name from $names, give it an index in the sorted array based on the number between the two -s, and add it to $sorted.
I used usort() to do this:-
$files = array('-10-filename.php', '-9-filename.php', '-8-filename.php', '-10-filename.php', '-1-filename.php', '-0-filename.php');
$cmp = function($a, $b){
list($mt, $num1, $name) = explode('-', $a);
list($mt, $num2, $name) = explode('-', $b);
return $num1 - $num2;
};
usort($files, $cmp);
var_dump($files);
Gave me this output:-
array
0 => string '-0-filename.php' (length=15)
1 => string '-1-filename.php' (length=15)
2 => string '-8-filename.php' (length=15)
3 => string '-9-filename.php' (length=15)
4 => string '-10-filename.php' (length=16)
5 => string '-10-filename.php' (length=16)
If you want them sorted in reverse just change the last line in the comparison function to:-
return $num2 - $num1;
As an aside, you may find a comparison of PHP sort functions useful.