PHP: one dimensional array to multidimensional array with element multiplication - php

Is there an easy way to transform a one dimensional array into multidimensional array and along with that add a certain element to the newly created sub-arrays?
The countrycode always has 2 digits and appears only once before a set of locations. That's the element that I'd like to duplicate and add it to every subarray as shown below.
Thanks in advance!
That's what I have:
0 => AT
1 => Vienna-S03-I01
2 => 28 Users
3 => Vienna-S03-I02
4 => 25 Users
5 => Vienna-S03-I03
6 => 24 Users
7 => AU
8 => Sydney-S01-I01
9 => 45 Users
10 => BE
11 => Brussels-S01-I01
12 => 30 Users
13 => Brussels-S01-I02
14 => 37 Users
That's what I'd like to have:
0 =>
0 => AT
1 => Vienna-S03-I01
2 => 28 Users
1 =>
0 => AT
1 => Vienna-S03-I02
2 => 25 Users
2 =>
0 => AT
1 => Vienna-S03-I03
2 => 24 Users
3 =>
0 => AU
1 => Sydney-S01-I01
2 => 45 Users
4 =>
0 => BE
1 => Brussels-S01-I01
2 => 30 Users
5 =>
0 => BE
1 => Brussels-S01-I02
2 => 37 Users

If there is a guarantee that array will always follow rule you demonstrated, then code is below. Otherwise few condition check should be added there to make sure that we have proper value type in every given $item.
$array = ['AT',
'Vienna-S03-I01',
'28 Users',
'Vienna-S03-I02',
'25 Users',
'Vienna-S03-I03',
'24 Users',
'AU',
'Sydney-S01-I01',
'45 Users',
'BE',
'Brussels-S01-I01',
'30 Users',
'Brussels-S01-I02',
'37 Users'];
$code='none';
$result=[];
$resIndex=-1;
$swing=false;
foreach($array as $item){
if (strlen($item)===2){
$code=$item;
}else{
if ($swing===false){
$resIndex++;
$result[$resIndex][]=$code;
}
$result[$resIndex][]=$item;
$swing=!$swing;
}
}
print_r($result);
?>

This is a very light method in terms of function calls and variables. isset() is a very swift function call, so there will be minimal drag there. The snippet is condensed by declaring multiple variables in a single line ($result[++$i][]=$k=$v;) and by incrementing the counter (++$i) inside of the result array declarations.
Code: (Demo)
$array = ['AT',
'Vienna-S03-I01',
'28 Users',
'Vienna-S03-I02',
'25 Users',
'Vienna-S03-I03',
'24 Users',
'AU',
'Sydney-S01-I01',
'45 Users',
'BE',
'Brussels-S01-I01',
'30 Users',
'Brussels-S01-I02',
'37 Users'];
$i=-1;
foreach($array as $v){
if(strlen($v)==2){ // check for new Country ID
$result[++$i][]=$k=$v; // preserve Country ID as $k, store as a new batch
}else{
if(isset($result[$i][2])){ // when three elements in current batch, start new...
$result[++$i][]=$k; // increment counter and store preserved Country ID
}
$result[$i][]=$v; // add $v to batch
}
}
var_export($result);
p.s. as a matter of further micro-optimization, you could swap out strlen() for another isset() call -- but that is a bit less intuitive:
$i=-1;
foreach($array as $v){
if(!isset($v[2])){ // check for existence of character at offset 2
$result[++$i][]=$k=$v;
}else{
if(isset($result[$i][2])){
$result[++$i][]=$k;
}
$result[$i][]=$v;
}
}
This is a functional approach that doesn't need to iterate for each element in the array. I just love array_splice() and its fantastic dual action of extracting elements and shortening the input array:
// array_splice modifies the original array (shortens it) and returns the removed elements
while($array){
if(strlen($array[0])==2){ // if the first value is a Country id
$id=$array[0]; // preserve id
$result[]=array_splice($array,0,3); // cut away and preserve first three elements
}else{
$result[]=[$id]+array_splice($array,0,2); // store id and first two elements
}
}
var_export($result);
...and finally, my DRYest method:
while($array){
if(strlen($array[0])==2){ // if the first value is a Country id
$id=array_splice($array,0,1);
}
$result[]=array_merge($id,array_splice($array,0,2)); // store preserved id and two elements
}
var_export($result);

Related

Sort a multi-dimensional array on two subarray values

I would like to sort an array on eta ASC then ab ASC.
Here's the input array:
$res_ubr=array(
"0"=>array(
"ab"=>"jdfd",
"bb"=>"iweuiru",
"eta"=>4
),
"1"=>array(
"ab"=>"dsdjdfd",
"bb"=>"iuiru",
"eta"=>5
),
"2"=>array(
"ab"=>"jdgfd",
"bb"=>"edfguiru",
"eta"=>2
),
"3"=>array(
"ab"=>"err",
"bb"=>"iuierru",
"eta"=>2
),
"4"=>array(
"ab"=>"fdfdf",
"bb"=>"dfdf",
"eta"=>3
)
);
Here is my expected result:
[
['ab'=>'err', 'bb'=>'iuierru', 'eta'=>2],
['ab'=>'jdgfd', 'bb'=>'edfguiru', 'eta'=>2],
['ab'=>'fdfdf', 'bb'=>'dfdf', 'eta'=>3],
['ab'=>'jdfd', 'bb'=>'iweuiru', 'eta'=>4],
['ab'=>'dsdjdfd', 'bb'=>'iuiru', 'eta'=>5]
]
This is what I've tried:
$res2=$res_ubr;
$temp=array();
foreach($res_ubr as $key=>$val){
foreach($res2 as $k=>$v){
if($val['eta']>$v['eta']){
$temp[]=$res_ubr[$key];
$res_ubr[$key] =$res_ubr[$k];
$res_ubr[$k]=$temp[];
}
}
}
print_r($res_ubr);
For context, the eta value is the time in minutes that the cab is expected to arrive.
You only need to assign a unique temporary key. There are two eta's with a value of 2 in your input array, so you will need to provide a "tie breaker". I am using the ab value to dictate the sorting order when two duplicate etas exist. I'm arbitrarily padding the eta values to an 8 digit number to ensure that numeric sorting occurs as intended. Without left padding with zeros, 10 would come before 2 because the temporary keys will be sorted as strings (one character at a time from left to right) and 1 comes before 2.
foreach($res_ubr as $sub){
$result[str_pad($sub["eta"],8,'0',STR_PAD_LEFT).$sub["ab"]]=$sub;
}
ksort($result);
var_export(array_values($result));
Because you say the eta are taxi arrival times, I'd bet your business that no eta is going to be in the tens of millions of minutes, so you can reduce the padding amount from 8 down to what ever is reasonably expected.
Keep in mind that if there is ever a duplicate temporary key (a matching pair of eta & ab), then my method will overwrite the first element with the second and you will have missing data. If this is a remote possibility, you should also append bb to the temporary key. If this, again, at risk of being a duplicate, you should add a randomly generated string of numbers to prevent your data being erased.
Output with eta ASC, ab ASC:
array (
0 =>
array (
'ab' => 'err',
'bb' => 'iuierru',
'eta' => 2,
),
1 =>
array (
'ab' => 'jdgfd',
'bb' => 'edfguiru',
'eta' => 2,
),
2 =>
array (
'ab' => 'fdfdf',
'bb' => 'dfdf',
'eta' => 3,
),
3 =>
array (
'ab' => 'jdfd',
'bb' => 'iweuiru',
'eta' => 4,
),
4 =>
array (
'ab' => 'dsdjdfd',
'bb' => 'iuiru',
'eta' => 5,
),
)

PHP, mysql_fetch_array and decompose an array into multiple arrays

I have a database table (QUEUE) like this:
queueId phoneNumber
1 340 000
1 340 111 1
1 340 222
2 332 000
2 332 111
3 421 000
3 421 111
3 421 222
I use this query:
SELECT * FROM queue ORDER BY queueId
and this php code:
while ($rowQueue = mysql_fetch_array($resultQueryQueue, MYSQL_ASSOC)) {
$queue[] = array(
'queueId' => $rowQueue['queueId'],
'phoneNumber' => $rowQueue['phoneNumber']
);
}
result is a array with 8 arrays (beacuse record are 8).
I would like to get an array that contains arrays as there are so many keys. In my example I would like to get 3 arrays, the first key 1, the second and the third with key 2 and key 3.
How can I make PHP? Is there any function that can help me?
I think of two way to do that (off course there will be few other).
One you can do with mysql query IF "queryId" is a foreign key you can get all the phoneNumber associated with specific queryId
SECOND: i am Guessing you will get a result from query is like this
$arr = array(
array(
'queryid' => 1,
'phonenumber'=>350000
),
array(
'queryid' => 1,
'phonenumber'=>350000
),
array(
'queryid' => 2,
'phonenumber'=>340001
),
array(
'queryid' => 2,
'phonenumber'=>340002
)
);
You can sort this by
$ret = array();
foreach ($arr as $k => $v)
{
$ret [$v['queryid']][]=$v['phonenumber'];
}
var_dump($ret);
key of this array will be 'queryid' and will have array of phonenumber related to that key
And also consider removing space from phone number.
Let me know if it worked. thanks

How can I efficiently generate an array of all possible alphabetical combinations of a given length of letters?

Imagine I have a bag of 26 scrabble tiles - one for each letter in the English alphabet.
My goal is to create an array of all possible strings up to n letters long. Say n=3.
Constraints:
Letters must always be in alphabetical order (ABC, not CBA; combinations, not permutations)
Strings must be <= n letters long (allow algorithm to break out of any loops at a given length)
Letters may not repeat (A, not AA)
How can I most efficiently generate this array in PHP?
In other words, how can I avoid brute force looping through all possible combinations and filtering out those that don't match the rules above?
If my alphabet only contained 3 letters — $alphabet = range('a','c'); — I'd expect output of an array of 7 items (3C1+3C2+3C3): [A,B,C,AB,AC,BC,ABC].
If my alphabet only contained 4 letters — $alphabet = range('a','d'); — I'd expect output of an array of 15 items (4C1+4C2+4C3+4C4): [A,B,C,D,AB,AC,AD,BC,BD,CD,ABC,ABD,ACD,BCD,ABCD]. But if I wanted to limit to only strings <= 3 letters long, then I would ignore ABCD resulting in only 14 items (4C1+4C2+4C3).
$alphabet = range('a','z');
print_r(generate_strings($alphabet,1));
// expected output: A,B,C,...Z
print_r(generate_strings($alphabet,2));
// expected output: A..Z, AB..AZ, BC..BZ, CD, ..YZ
print_r(generate_strings($alphabet,3));
// expected output: A..Z, AB..YZ, ABC..XYZ
print_r(generate_strings($alphabet,10));
// expected output: A .. JKLMN .. AGKQRZ .. QRSTUVWXYZ
// ^ ^ ^10 character max, no repeats
// | still alphabetical order
// alphabetical order
function generate_strings($alphabet,$max_word_length) {
// how can I efficiently generate this array
// without brute force looping through all of
// the invalid and duplicate items like AA and CBA?
return $array_of_all_possible_strings;
}
I thought this looked like fun. Here's my attempt at it, for what it's worth:
function recurse($letters, &$words, $start, $end, $depth, $prefix = "") {
$depth--;
for ($i = $start; $i < $end; $i++) {
$word = $prefix . $letters[$i];
$words[] = $word;
if ($depth) recurse($letters, $words, ++$start, $end, $depth, $word);
}
}
function generate_strings($letters, $max_word_length) {
$words = array();
recurse($letters, $words, 0, count($letters), $max_word_length);
return $words;
}
Don't know php, but the algorithm is clear:
Sort the N letters (if not already) into an array.
Maintain two dynamic arrays: the list of all combinations of length N or less (what you want), and the list of all strings of length N-1 or less.
Loop backwards through the character array from the last character.
Add the ith character to the list of all strings as a single-character string. Also add to the list of strings of length N-1 if N > 1.
Now loop through the list of strings of length N-1 or less, going from the start of the array to the current end of the array.
For each shorter string, create a new string by prepending the ith character.
Add this new string to the list of all strings.
And if of length N -1 or less, add to the list of shorter strings. (Careful with iterators here - you don't want to visit the string you just added in this inner loop.)
Return the list of strings.
I edited this similar post of mine today (since I realized that I originally posted an incorrect method) and while researching the topic, I found your question interesting.
Don'tPanic's recursive method seems to work as far I can tell. I just thought I'd post a non-recursive "stacking" method based on my linked method. I believe there is little performance difference between our solutions, maybe you'll find mine easier to read (maybe not).
Code: (Demo)
function permStacker($array,$length){
$stack=[[]]; // declare intitial empty element
for($x=0; $x<$length; ++$x){ // limit the total number of passes / max string length
foreach($stack as $combination){
foreach(array_diff($array,range('a',end($combination))) as $left){ // do not include iterate letter that come earlier than rightmost letter
$merge=array_merge($combination,[$left]);
$stack[implode($merge)]=$merge; // keys hold desired strings, values hold subarray of combinations for iterated referencing
}
}
}
unset($stack[0]); // remove initial empty element
return array_keys($stack); // return array of permutations as space delimited strings
}
$permutations=permStacker(range('a','h'),4);
echo 'Total Permutations: ',sizeof($permutations),"\n";
var_export($permutations);
Output (truncated):
Total Permutations: 162
array (
0 => 'a',
1 => 'b',
2 => 'c',
3 => 'd',
4 => 'e',
5 => 'f',
6 => 'g',
7 => 'h',
8 => 'ab',
9 => 'ac',
10 => 'ad',
11 => 'ae',
12 => 'af',
13 => 'ag',
14 => 'ah',
15 => 'bc',
16 => 'bd',
17 => 'be',
18 => 'bf',
19 => 'bg',
20 => 'bh',
21 => 'cd',
22 => 'ce',
23 => 'cf',
24 => 'cg',
25 => 'ch',
26 => 'de',
...
126 => 'afgh',
127 => 'bcde',
128 => 'bcdf',
129 => 'bcdg',
130 => 'bcdh',
131 => 'bcef',
132 => 'bceg',
133 => 'bceh',
134 => 'bcfg',
135 => 'bcfh',
136 => 'bcgh',
137 => 'bdef',
138 => 'bdeg',
139 => 'bdeh',
140 => 'bdfg',
141 => 'bdfh',
142 => 'bdgh',
143 => 'befg',
144 => 'befh',
145 => 'begh',
146 => 'bfgh',
147 => 'cdef',
148 => 'cdeg',
149 => 'cdeh',
150 => 'cdfg',
151 => 'cdfh',
152 => 'cdgh',
153 => 'cefg',
154 => 'cefh',
155 => 'cegh',
156 => 'cfgh',
157 => 'defg',
158 => 'defh',
159 => 'degh',
160 => 'dfgh',
161 => 'efgh',
)

create unique ids from multidimensional array

I have an array with small example below
Array (2)
system => Array (1)
system_id => 3
proc => Array (4)
proc_id => 5
proc_or => 1
proc_owner => 7751
results => Array (7)
0 => Array (5)
process_id => 101
process => 1335
process_name => xa
process_owner => xo
rating => 67.554
1 => Array (6)
process_id => 122
process => 1335
process_name => xa
process_owner => xo
rating => 33.554
proc_rel => xf
2 => Array (5)
process_id => 101
process => 1227
process_name => xd
process_owner => xa
rating => 123.78
3 => Array (8)
process_id => 101
process => 1291
process_name => xa
process_owner => xo
rating => 64.241
proc_rel => xf
proc_rel_id => 1474
proc_rel_owner => xm
I need to be able to organise this so that it contains only unique process_id values, so results 2 and 3 would be removed and the rating for that remaining unique id is the sum of all ratings for the records with that id so the results 0 rating id would become the sum of results 0 2 and 3.
There are thousands of records so its not practyical to do it without some sort of automated loop
I was thinking of maybe creating a new array and as its being processed
If the process_id is not in new array add it and all related data
If the process id is already in the array just add(+) the value of rating to the existing value.
I have tried to adapt a couple of loops that ive found but they dont seem to work properly.
Not sure if this is the way to go and Im not sure how to do it anyway so any suggestions greatly appreciated.
If the data set is big, then better try to adjust your database query or change file structure when its generated. If it isnt possible then the only rational way is to create new array:
$newArray = ();
foreach($array['proc']['results'] as $result) {
if (isset($newArray[$result['process_id']]) {
$newArray[$result['process_id']]['rating'] += $result['rating'];
} else {
$newArray[$result['process_id']] = $result;
}
}

PHP Custom array sort filenames by year and month, newest first

I've got an array of files which looks like this:
array (
0 => 'scpt-01-2010.phtml',
1 => 'scpt-01-2011.phtml',
2 => 'scpt-02-2010.phtml',
3 => 'scpt-02-2011.phtml',
4 => 'scpt-03-2010.phtml',
5 => 'scpt-04-2010.phtml',
6 => 'scpt-05-2010.phtml',
7 => 'scpt-06-2010.phtml',
8 => 'scpt-07-2010.phtml',
9 => 'scpt-08-2010.phtml',
10 => 'scpt-09-2010.phtml',
11 => 'scpt-10-2010.phtml',
12 => 'scpt-11-2010.phtml',
13 => 'scpt-12-2010.phtml',
);
How can I sort it so that 2011 files appear first, in order of month (so it should lead with scpt-02-2011.phtml)?
I've tried the main sorting functions like natsort, rsort, arsort etc but I'm not getting anywhere fast!
Thanks in advance.
function customSort($a, $b) {
// extract the values with a simple regular expression
preg_match('/scpt-(\d{2})-(\d{4})\.phtml/i', $a, $matches1);
preg_match('/scpt-(\d{2})-(\d{4})\.phtml/i', $b, $matches2);
// if the years are equal, compare by month
if ($matches2[2] == $matches1[2]) {
return $matches2[1] - $matches1[1];
// otherwise, compare by year
} else {
return $matches2[2] - $matches1[2];
}
}
// sort the array
usort($array, 'customSort');
This method uses usort() to sort the array, passing the name of our comparison function.
http://php.net/usort
Use usort(), it allows you to write your own callback and sort your own way.
There is no need to roll out the regex engine for this task. You need to explode on the hyphens, compare the elements in reverse order, and sort in descending order. The following translates rather literally as long as you understand that return $b <=> $a; means "sort descending". Since all of your file extensions are the same, the extensions may remain attached to the year value without damaging accuracy.
Code: (Demo)
$files = [
0 => 'scpt-01-2010.phtml',
1 => 'scpt-01-2011.phtml',
2 => 'scpt-02-2010.phtml',
3 => 'scpt-02-2011.phtml',
4 => 'scpt-03-2010.phtml',
5 => 'scpt-04-2010.phtml',
6 => 'scpt-05-2010.phtml',
7 => 'scpt-06-2010.phtml',
8 => 'scpt-07-2010.phtml',
9 => 'scpt-08-2010.phtml',
10 => 'scpt-09-2010.phtml',
11 => 'scpt-10-2010.phtml',
12 => 'scpt-11-2010.phtml',
13 => 'scpt-12-2010.phtml',
];
usort($files, function ($a, $b) {
return array_reverse(explode('-', $b, 3)) <=> array_reverse(explode('-', $a, 3));
});
var_export($files);
Output:
array (
0 => 'scpt-02-2011.phtml',
1 => 'scpt-01-2011.phtml',
2 => 'scpt-12-2010.phtml',
3 => 'scpt-11-2010.phtml',
4 => 'scpt-10-2010.phtml',
5 => 'scpt-09-2010.phtml',
6 => 'scpt-08-2010.phtml',
7 => 'scpt-07-2010.phtml',
8 => 'scpt-06-2010.phtml',
9 => 'scpt-05-2010.phtml',
10 => 'scpt-04-2010.phtml',
11 => 'scpt-03-2010.phtml',
12 => 'scpt-02-2010.phtml',
13 => 'scpt-01-2010.phtml',
)
To rephrase, for a file like scpt-02-2011.phtml, the explosion creates:
['scpt', '02', '2011.phtml']
then it is reversed to become:
['2011.phtml', '02', 'scpt']
then each element is progressively compared against the other filename's respective element in a DESC fashion.

Categories