Create a 3D array from a list - php

I have the following list:
1945/01/05 BA 87 34 1 59 50
1945/01/05 CA 18 17 45 49 82
1945/01/13 BA 6 66 1 16 48
1945/01/13 CA 40 60 32 50 80
and so on....
I want to arrange them as an array like the following:
$array['BA'][0][0] = 87;
$array['BA'][0][1] = 34;
$array['BA'][0][2] = 1;
$array['BA'][0][3] = 59;
$array['BA'][0][4] = 50;
$array['CA'][0][0] = 18;
$array['CA'][0][1] = 17;
$array['CA'][0][2] = 45;
$array['CA'][0][3] = 49;
$array['CA'][0][4] = 82;
$array['BA'][1][0] = 6;
$array['BA'][1][1] = 66;
$array['BA'][1][2] = 1;
$array['BA'][1][3] = 16;
$array['BA'][1][4] = 48;
$array['CA'][1][0] = 48;
$array['CA'][1][1] = 60;
$array['CA'][1][2] = 32;
$array['CA'][1][3] = 50;
$array['CA'][1][4] = 80;
I know that I can use preg_split('/\s+/', $list); to create an array, but how can I create a 3D Array from this list?
Thank you for your help

Try something like this:
Updated the code so you can also load a file since you have memory issues.
Consider: If loading the amount of data already creates memory issues your end result, the array, might also generate memory issues! Since that's even more data!
So this will read per line so it does not load the total file in memory directly. But since you have memory issues you should likely not write to an array but to a database for example. Otherwise the memory issue will keep coming.
<?PHP
/*
* Example when small amount of data is in a string
*/
/*
//your input
$str='1945/01/05 BA 87 34 1 59 50
1945/01/05 CA 18 17 45 49 82
1945/01/13 BA 6 66 1 16 48
1945/01/13 CA 40 60 32 50 80';
//we work per line
$lines=explode("\n", $str);
//loop each line
foreach($lines AS $line) {
*/
/*
* Example when big amount of data is in a file
*/
//this will contain the end result
$result=array();
$filename='lines.txt'; //contains the data like in your question
$fp = fopen($filename,'r');
while($line=fgets($fp)) {
//explode each line on space so we get the different fields
$fields=explode(' ', $line);
//we remove the date, not needed
unset($fields[0]);
//we get the key (BA/CA/etc) and remove it also
$key=$fields[1];
unset($fields[1]);
//we write the result to the array
//using array_values so the indexes are from 0-4 again
//because we removed items
$result[$key][]=array_values($fields);
}
fclose($fp);
//show the result in html
echo '<pre>';
print_r($result);
THE QUESTION CHANGED
This does answer the question asked in the comments, not the actual question.
<?PHP
/*
* Example when big amount of data is in a file
*/
//this will contain the end result
$result=array();
$filename='lines.txt'; //contains the data like in your question
$fp = fopen($filename,'r');
while($line=fgets($fp)) {
//explode each line on space so we get the different fields
$fields=explode(' ', $line);
//we remove the date, not needed
unset($fields[0]);
//we get the key (BA/CA/etc) and remove it also
$key=$fields[1];
unset($fields[1]);
//We start counting the numbers
foreach($fields AS $nr) {
$nr=trim($nr);
if(empty($result[$key][$nr])) {
$result[$key][$nr]=1;
}else{
$result[$key][$nr]++;
}
}
}
fclose($fp);
//show the result in html
echo '<pre>';
print_r($result);

<?php
$a = '1945/01/05 BA 87 34 1 59 50
1945/01/05 CA 18 17 45 49 82
1945/01/13 BA 6 66 1 16 48
1945/01/13 CA 40 60 32 50 80';
$array = array();
$list = explode("\n", $a);
echo '<pre>';
print_r($list);
echo '</pre>';
$first_index = 0;
$counter = 0;
foreach($list as $key=>$value) {
if($counter >=2) {
$first_index++;
$counter= 0;
}
$row = explode(" ",$value);
$k = $row[1];
for($i=2; $i< count($row); $i++) {
$array[$k][$first_index][] = $row[$i];
}
$counter++;
}
echo '<pre>';
print_r($array);
echo '</pre>';
?>

Related

Assigning Positions to Students, PHP

I am still a novice at PHP scripting.
I have an Array
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
From the associative array, the key are the student's exam no and the values are the student's scores. Then I used the 2 inbult PHP functions array_keys and array_values to separate the exam nos from the scores.
$exam_nos=(array_keys($students));
$marks=(array_values($students));
Then I passed the $marks array through the code below:
$i=0;
$occurrences = array_count_values($marks);
$marks = array_unique($marks);
echo '<table border="1">';
foreach($marks as $grade) {
if($grade == end($marks))$i += $occurrences[$grade]-1;
echo str_repeat('<tr><td>'.$grade.': '.($i+1).'</td></tr>',$occurrences[$grade]);
$i += $occurrences[$grade];
}
echo '</table><br />';
output:
94: 1
94: 1
94: 1
91: 4
89: 5
83: 6
65: 7
41: 8
38: 9
37: 11
37: 11
And this is closer to what I want; to rank the scores such that if a tie is encountered, 1 or more positions are skipped, occurs at the end the position the items at the end are assigned a position equivalent toi the total number of ranked items. However, it would be much helpful if this could be done without separating the Array into 2 ...
Questions:
(1) I am pulling my hair how, from the $student array I could have something like:
Exam No Score Position
1201 94 1
1210 94 1
1203 94 1
1200 91 4
1205 89 5
1209 83 6
1206 65 7
1202 41 8
1207 38 9
1204 37 11
1208 37 11
(2) I would like to be able to pick any student by exam no and be able to echo or print out her position e.g
the student 1207 is number 9.
I think I need to capture the postions in a variable, but how do I capture them? Well I don't know!
Could the experts help me here with a better way to achieve my 2 goals (please see questions 1 and 2)? I will try any suggestion that will help me disolve the 'metal blockage' I have hit.
If you're pulling out the students from a database (mentioned in the comments), you could retrieve them with the desired format directly using SQL.
However, I'm going to assume that that's not an option. You could do as follows:
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
arsort($students);// It orders high to low by value. You could avoid this with a simple ORDER BY clause in SQL.
$result = array();
$pos = $real_pos = 0;
$prev_score = -1;
foreach ($students as $exam_n => $score) {
$real_pos += 1;// Natural position.
$pos = ($prev_score != $score) ? $real_pos : $pos;// If I have same score, I have same position in ranking, otherwise, natural position.
$result[$exam_n] = array(
"score" => $score,
"position" => $pos,
"exam_no" => $exam_n
);
$prev_score = $score;// update last score.
}
$desired = 1207;
print_r($result);
echo "Student " . $result[$desired]["exam_no"] . ", position: " . $result[$desired]["position"] . " and score: ". $result[$desired]["score"];
Hope it helps you.
I would use a custom object to process the students individually and store them in an array.
$students = array(1201=>94,1203=>94,1200=>91, 1205=>89, 1209=>83, 1206=>65, 1202=>41, 1207=>38,1208=>37, 1204=>37,1210=>94);
arsort($students); // Sort the array so the higher scores are on top.
$newStudents = array();
$pos = 0;
$count = 0;
$holder = -1; // Assuming no negative scores.
foreach($students as $k=>$v){
$count++; // increment real counter
if($v < $holder || $holder == -1){
$holder = $v;
$pos = $count;
}
$newStudents[] = makeStudent($pos, $v, $k);
// If you want the exam # as the array key.
// $newStudents[$k] = $student;
}
$newStudents = fixLast($newStudents);
// outputs
print_r($newStudents);
foreach($newStudents as $v){
echo "position : " . $v->position . "<br>";
echo "score : " . $v->score . "<br>";
echo "exam : " . $v->exam . "<br>";
}
function makeStudent($pos, $score,$examNo){
$student = new stdClass(); // You could make a custom, but keeping it simple
$student->position = $pos;
$student->score = $score;
$student->exam = $examNo;
return $student;
}
function fixLast($students){
$length = count($students) -1;
$count = 0;
$i = $length;
while($students[$i]->position == $students[--$i]->position){
$count++;
}
for($i = 0; $i <= $count; $i++){
$students[$length - $i]->position = $students[$length - $i]->position + $count;
}
return $students;
}

fill 2 dimensional array random in a bingo way

I've got these two functions:
function drawNumber($drawnNumbers){
$unique = true;
while ($unique == true){
$number = mt_rand(10, 69);
if (!in_array($number, $drawnNumbers)){
return $number;
$unique = false;
}
}
}
fillCard(); ?>
It's a bingo game. The card gets filled with random Numbers. But I can't get it like this:
column column column column column column
row 10 13 16 14 16 19
row 24 26 28 29 23 21
row 36 33 39 30 31 35
row 46 48 42 45 43 47
row 59 56 51 52 58 50
row 60 65 68 62 61 67
So I would like to have the first row with numbers from 10 to 19
the second row from 20 to 29 and so on.
I tried like this
<?php drawnNumber(): $number = mt_rand(0,9);
fillArray(): $number = $row . $number; ?>
But that doesn't work, because there are double numbers in the card.
So before that I tried it in a different way,with in_array:
<?php
function fillCard(){
$card = array();
/* fill card with random numbers */
for($i = 0, $min = 10, $max = 19; $i < 6; $i++, $min+=10, $max += 10)
{
for($j = 0; $j < 6; $j++)
{
$number = mt_rand($min,$max) ;
if(!in_array($number, $card){
$card['row' . $i]['column' . $j]['number'] = $number;
$card['row' . $i]['column' . $j]['found'] = 0;
}
}
}
var_dump($card);
return $card;
} ?>
But there are still double random numbers in the card.
I tried a few other thinks in the last two weeks, but I just can't get it to work together.
If one thing succeeds the other thing fails.
I can get the random numbers but not unique random numbers in the card.
I hope someone can help me.
(for extra information: it's a bingo game. So drawnNumber() are the "balls", which get drawn
and stored in the array $drawnNumbers, they also are unique random numbers. fillCard() is the
function that fills the bingo card and checks if $drawNumber is in $card)
I would appreciate some help, if someone can tell me how to get it to work. Maybe in
an algorithm way or else some code?
Thank you in advance.
In general, you draw from some kind of box, right? So do the same, have an array with all available numbers and once you get a random number out of it, remove it, so the next time you search for a number, you will only pick from the remaining ones. Small example:
a[0] = 1
a[1] = 2
a[2] = 3
a[3] = 4
we pick a random number between 0 and 3 inclusive (0 and the length - 1 of a that is). Let's say we picked index 2, then a will look like:
a[0] = 1
a[1] = 2
a[2] = 4
Now if you draw a number between 0 and 2 (note that you take the length - 1 of a!), you won't re-pick the already chosen number in any way thus giving you unique numbers ALL the time.
P.S. this is a simplified version, but now if you can apply that to yourself and, for your example, create several arrays you will pick from.
The simplest way would be to have an additional flat array to keep track, and loop mt_rand
Here's an example of the meat of things:
$drawn = array();
// Loop around until you have a new number in the desired range
do {
$number = mt_rand($min,$max);
} while(in_array($number, $drawn));
// And save it to the $drawn array
$drawn[] = $rand;
To clarify, the above snippet (without the initialization of $drawn) is meant to replace the line
$number = mt_rand($min,$max) ;
in your code.
define('NB_ROWS', 6);
define('NB_COLS', 6);
$rows = range(10, NB_ROWS * 10, 10);
$bingo = array();
foreach($rows as $rowIndex)
{
$availNumbers = range(0, 9);
$line = array();
for($cellIndex = 0; $cellIndex < NB_COLS; $cellIndex++)
{
// pick a random value among remaining ones for current line
$numIndex = rand(0, count($availNumbers)-1);
list($value) = array_splice($availNumbers, $numIndex, 1);
$line[] = $value + $rowIndex;
}
$bingo[] = $line;
}
print_r($bingo);

Creating files based on the line number for printing

I have following data extracted from the MySQL database to create labels.
BREAK
Name:RAJ
Company:ABC
Order Number:101
Order Details:
Item1
Item20
Item3
BREAK
Name:RAJ
Company:ABC
Order Number:101
Order Details:
2 x Item1
2 x Item2
2 x Item3
BREAK
Name:RAJ
Company:ABC
Order Number:101
Order Details:
5 x Item4
5 x Item5
5 x Item2
I wrote some code to find the position of BREAK in PHP and it can generate lines number like below.
2
14
26
36
I want a file for the contents that are between line 2 and 14 in one file and 26 to 36 in one file. I am working in php and tried using sed from shell_exec function however if I read this output and generate sed command, I don't get first 2 number together.
What I am expecting is below.
sed -n 2,14p file1.txt
sed -n 26,36p file2.txt
Any suggestion either in php or shell script?
Use array_slice() for getting ranges in an array.
My solution is very hard coupled to your requirements, meaning every first line is a starting range number and the following the end range.
// the lines from which will be read
$lines = "1
5
16
26";
// split these numbers above into an array
$lines = explode(PHP_EOL, $lines);
// the source file where the ranges will be taken off
$file = file('file.txt');
for($i = 0; $i < count($lines); $i+=2) {
$rangeStart = $lines[$i];
$rangeLength = $lines[$i+1] - $lines[$i];
// slice out the ranges, beware the second parameter is the length not the offset!
$ranges[] = array_slice($file, $rangeStart, $rangeLength);
}
print_r($ranges);
But it would be a lot easier to do it automatically on your source file/text/string (?) directly, if this is possible.
$file = file('file.txt');
$len = count($file);
$output = array();
$current = array();
for($i = 0; $i < $len; $i++) {
$data = trim($file[$i]);
if ($data != 'BREAK') {
$current[] = $data;
} else {
$output[] = implode(PHP_EOL, $current);
$current = array();
}
}
print_r($output);

Generate Distinct Combinations PHP [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
I need an efficient algorithm to generate distinct combinations(not allowed to repeat). Each combination has 5 distint numbers(different numbers) that ranges between 1 and 99. The result must be stored in an array. If possible I would like the numbers and range allowed to be customized. The order of number doesn't matter(01 02 03 = 03 01 02)
Ex.:
01 02 03 04 05
02 03 04 05 06
...
Does anyone could help me to build it? I would like to pick up some random combinations from the array. Nowdays I am generating random combinations using mt_rand but It takes too much time, SO SLOW! I believe happens to repeat so often then takes time to generate new one and new one...
I threw this together quickly, seems to work.
<?php
$range_low = 1;
$range_hi = 99;
$num_sets = 10;
$set = generateSet($range_low, $range_hi, $num_sets);
print_set($set);
function generateSet($range_low, $range_hi, $numSets = 5, $numPerSet = 5)
{
$return = array();
$numbers = array();
for($i = $range_low; $i <= $range_hi; ++$i) {
$numbers[] = $i;
}
for ($s = 0; $s < $numSets; ++$s) {
$set = array_values($numbers);
shuffle($set);
$return[$s] = array();
for ($i = 0; $i < $numPerSet; ++$i) {
$val = array_shift($set);
$return[$s][] = $val;
}
}
return $return;
}
function print_set($set)
{
foreach($set as $subset) {
foreach($subset as $value) {
echo str_pad($value, 2, '0', STR_PAD_LEFT) . ' ';
}
echo "\n";
}
}
Sample output:
90 75 89 43 57
24 54 38 35 10
77 21 55 33 83
37 15 61 09 44
25 31 85 17 20
48 37 45 13 20
82 70 74 64 72
07 24 33 64 45
34 13 39 33 05
13 77 87 70 64
To Fisher-Yates shuffle the array, see this comment on shuffle for a function you could use in place of shuffle.
Hope that helps.
If you really absolutely need a full set of every possible combination:
function combinations($set,$length) {
$combinations = array();
$setCount = count($set);
for($i = 0, $n = $setCount; $i <= ($n - $length); $i++) {
$combination = array();
$combination[] = $set[$i];
if($length > 1) {
$combination = array_merge(
$combination,
combinations(array_slice($set,1+$i), $length-1)
);
}
$combinations[] = $combination;
}
return $combinations;
}
$allYourNumbers = range(1,99);
$allCombinations = combinations($allYourNumbers, 5);
Then you can shuffle $allCombinations and extract as many as you want, but you'll need a lot of memory and a lot of time... doing this can never be efficient
Here's the simple code that should run rather fast and do what you describe.
$numbers = range(1, 99); // numbers to pick from
$length = 5; // amount of items in the set
$sets_amount = 15; // amount of sets you want to generate
shuffle($numbers); // randomize
function get_set($length, &$numbers) {
return array_splice($numbers, 0, $length);
}
for ($i = 0; $i < $sets_amount; $i++)
print_r(get_set($length, $numbers));
Note: it only works when you need a few combinations. You don't state that you want all of the possible ones, so I thought if you need just a bunch of them - here's very quick and easy way to do it.
For a bit slower (the more you generate - the slower it goes), but that generates any amount of sets, you can use this code.
$numbers = range(1, 99); // numbers to pick from
$length = 5; // amount of items in the set
$sets_amount = 200; // amount of sets you want to generate
$existing = array(); // where we store existing sets
$shuffle_period = count($numbers) - $length - 1; // how often we re-randomize the elements
$j = 0;
for ($i = 0; $i < $sets_amount; $i++, $j++) {
if (!($i % $shuffle_period)) {
shuffle($numbers); // randomize at first go and on $shuffle_period
$j = 0;
}
do {
$arr = array_slice($numbers, $j, $length);
} while (in_array($arr, $existing));
$existing[] = $arr;
}
print_r($existing);

PHP - create a spiral

assuming I have $rows = 4, and $cols = 4;, How do I create a array with 16 elements in a spiral order, like:
1, 2, 3, 4,
12,13,14,5,
11,16,15,6,
10,9, 8, 7,
My solution is quite verbose, because the intent is for you to examine it and learn how it works.
<?php
/**
* Creates a 2D array with the given dimensions,
* whose elements are numbers in increasing order
* rendered in a 'spiral' pattern.
*/
function createSpiral($w, $h) {
if ($w <= 0 || $h <= 0) return FALSE;
$ar = Array();
$used = Array();
// Establish grid
for ($j = 0; $j < $h; $j++) {
$ar[$j] = Array();
for ($i = 0; $i < $w; $i++)
$ar[$j][$i] = '-';
}
// Establish 'used' grid that's one bigger in each dimension
for ($j = -1; $j <= $h; $j++) {
$used[$j] = Array();
for ($i = -1; $i <= $w; $i++) {
if ($i == -1 || $i == $w || $j == -1 || $j == $h)
$used[$j][$i] = true;
else
$used[$j][$i] = false;
}
}
// Fill grid with spiral
$n = 0;
$i = 0;
$j = 0;
$direction = 0; // 0 - left, 1 - down, 2 - right, 3 - up
while (true) {
$ar[$j][$i] = $n++;
$used[$j][$i] = true;
// Advance
switch ($direction) {
case 0:
$i++; // go right
if ($used[$j][$i]) { // got to RHS
$i--; $j++; // go back left, then down
$direction = 1;
}
break;
case 1:
$j++; // go down
if ($used[$j][$i]) { // got to bottom
$j--; $i--; // go back up, then left
$direction = 2;
}
break;
case 2:
$i--; // go left
if ($used[$j][$i]) { // got to LHS
$i++; $j--; // go back left, then up
$direction = 3;
}
break;
case 3:
$j--; // go up
if ($used[$j][$i]) { // got to top
$j++; $i++; // go back down, then right
$direction = 0;
}
break;
}
// if the new position is in use, we're done!
if ($used[$j][$i])
return $ar;
}
}
/**
* Assumes the input is a 2D array.
*/
function print2DGrid($array) {
foreach ($array as $row) {
foreach ($row as $col) {
print sprintf("% 2d ", $col);
}
print "\n";
}
}
$width = 12;
$height = 8;
print2DGrid(createSpiral($width, $height));
?>
Here it is in action, giving the following output:
0 1 2 3 4 5 6 7 8 9 10 11
35 36 37 38 39 40 41 42 43 44 45 12
34 63 64 65 66 67 68 69 70 71 46 13
33 62 83 84 85 86 87 88 89 72 47 14
32 61 82 95 94 93 92 91 90 73 48 15
31 60 81 80 79 78 77 76 75 74 49 16
30 59 58 57 56 55 54 53 52 51 50 17
29 28 27 26 25 24 23 22 21 20 19 18
Hope this helps.
Use a vector and boolean values. Iterate on the X axis to start with until you reach the end of the row. Set the value to true for each position as you traverse it. Anytime you reach a border or boolean true, turn right.
So on the first row your "vector" would change the X increment to zero and the Y increment to 1. Then you would check the value of the increment at each turn and accommodate the 4 scenarios.
This is the first way I thought of.
The second way would not use booleans, but would instead simply keep track of how many columns or rows are left in front of the cursor on its path, decreasing the X or Y remaining on each turn. The rest of the logic would remain the same.
When you reach the center, you will hit a loop where there are no more iterations possible. You can catch this by verifying the number of possibilities around the square, or simply counting down from N number of total squares from where you began, and stopping when the number hits zero.
You can use the above method for a square of any size.
The algorith is not very dificult. You have to iterate from 1 up to $rows*$cols. During the iteration, you have to calculate the position of the current number in the matrix ($x,$y). The first on will be in (1,1) of course. The following one will be ($x+1,$y) because you are heading east. So, the position of each number is base on the position of the previous one and the currect direction.
The tricky part of the algorith is to calculate the direction. But if you look at it, you will see that the direction changes clockwise each time you bump into a used cell, or you get out of bounds.
All in all, this is going to be ~30 lines of PHP code.
I hope this helps.
There is a similar challenge in PHP golf: http://www.phpgolf.org/challenge/Spiral. Somebody solved it in only 130 characters!

Categories