Grouping multi-dimensional arrays in PHP - php

I can't focus right now and my mind keeps playing tricks on me with the solution to this.
I've tried a multitude of options but they keep not working.. Sigh.
Say I have a string as such; a.b.c|a.b.d|a.b.e|f|f.g|f.h|i, I want to create a new array (or object) as the following
A > B > C, D, E
F > G, H
I
With > being a nested array, , being an element in the parent array.
These should be able to continue nesting a multitude of times, e.g. A > B > C > D > E > F, D
Any guidance? I've tried exploding the string and then those strings to an array - this array holds A > B > C, A > B > D, A > B > E etc., I just can't get my head around to how to combine them efficiently.
I started with just looping over each element in the array and checking if the parents' key exists, but that was failing. Any help is appreciated as I'm incredibly tired, quite shocking I can do such a simple task.

<?php
// read line from standard input
$line = trim(fgets(STDIN));
echo "Line: $line\n"; // debug
// split lines by |
$segments = explode('|', $line);
print_r($segments); // debug
// prepare output array
$md_array = array();
// walk through the segments
foreach($segments as $segment) {
// set pointer to output array
$current = &$md_array;
// split segment by .
$tokens = explode('.', $segment);
print_r($tokens); // debug
foreach($tokens as $token) {
echo "working on $token\n";
// if key is not included in the array, create empty array
if( ! array_key_exists($token, $current) ) {
$current[$token] = array();
}
// pass the pointer to following sub-section
$current = &$current[$token];
}
}
// print out the output
print_r($md_array);
?>
Testing the script
echo "a.b.c|a.b.d|a.b.e|f|f.g|f.h|i" | php test.php
Output
Array
(
[a] => Array
(
[b] => Array
(
[c] => Array
(
)
[d] => Array
(
)
[e] => Array
(
)
)
)
[f] => Array
(
[g] => Array
(
)
[h] => Array
(
)
)
[i] => Array
(
)
)

Want to thank Richard for his solution, which allowed me to achieve my aim.
See the code below:
public static function permissions($permList) {
$segments = explode('|', $permList);
$md_array = array();
foreach($segments as $segment) {
$current = &$md_array;
$tokens = explode('.', $segment);
$x = 0;
foreach($tokens as $token) {
if(!array_key_exists($token, $current) ) {
if(count($tokens) > $x+1)
$current[$token] = array();
else
$current[] = $token;
}
if(count($tokens) > $x+1)
$current = &$current[$token];
$x++;
}
}
return $md_array;
}
If I input a.b.c|a.b.d|a.b.e|f|f.g|f.h|i, I am returned with the expected output above.
Many thanks.

Related

Add params to array request

I have a pretty large array that I would need to parse, but the request changes depending on enabled/disabled parameters.
For example:
$array['a'][1]['b'][1]['c'][1] = 'asd';
$str = $array['a'][1];
dd($str);
This would give me:
Array
(
[b] => Array
(
[1] => Array
(
[c] => Array
(
[1] => asd
)
)
)
)
Which, of course, is correct. But now, if I know, that I need also the next parameter, I would need to add that like $str = $array['a'][1]['b'];.
But since there are way too many combinations, I wondered, if I can construct the call manually, something like this":
$str = $array['a'][1];
if ($b) {
$str .= ['b'][1];
}
if ($c) {
$str .= ['c'][1];
}
dd($str);
Any hints will be appreciated.
PS: I know I can do this with eval, but was really looking for a better solution:
eval("\$str = \$array$str;");
dd($str);
It can be done with Reference
$string = "['a'][1]['b'][1]";
// Maybe, not optimal, but it's not the point of the code
preg_match_all('/\[\'?([^\]\']+)\'?\]/', $string, $steps);
// "Root" of the array
$p = &$array;
foreach($steps[1] as $s) {
// Next step with current key
if (isset($p[$s]) && is_array($p)) $p = &$p[$s];
else throw new Exception("No such item");
}
// Target value
$str = $p;
demo

Codeigniter : how to multiply matrices array data from db?

well i have a problem with logic of matrices multiplication in php; the data comes from the database in the form of an one dimension array (Array ( [0] => 1.0000 [1] => 0.5000 [2] => 3.0000 [3] => 2.0000 [4] => 1.0000 [5] => 5.0000 [6] => 0.3333 [7] => 0.2000 [8] => 1.0000 ) ), that I need to transform into a matrice. The dimension of the original array is a square number (in this case 9), so the result matrice will have two equal dimensions, both equal to the square root (3) of the original data array.
The result matrice has to be multiplied by itself, using the pattern in the image below:
I have made some research before, but none of them were right.
i have the following code i used in the model to create the algorithm:
function hitung_matriks(){
$query = $this->db->query("select * from banding b
inner join kriteria a on a.Kd_Kriteria1 = b.Kd_Kriteria1");
$dt_matriks = $query->result();
$data = array();
foreach($dt_matriks as $a){
$data[] = $a->Nilai_Banding;
}
echo "<pre>";
print_r($data);
echo "</pre>";
$c = array();
for($i=1;$i<=sqrt(count($data));$i++){
$d = array();
$isi=0;
for($j=1;$j<=sqrt(count($data));$j++){
$isi = $data[$i][$j] * $data[$j][$i];
$d[] = $isi;
}
$c[] = $d;
}
echo "<pre>";
print_r($c);
echo "</pre>";die();
}
and the result of each array comes 0.
I want to make this code works to be like this :
please help me :'(
Updated answer:
Way to transform $data array into a matrice:
$data = array(1.0000, 0.5000, 3.0000, 2.0000, 1.0000, 5.0000, 0.3333, 0.2000, 1.0000);
$data2 = array();
$j = 0;
$k = 0;
for($i=0;$i<count($data);$i++){
if($j < sqrt(count($data)) ){
$data2[$j][$k] = $data[$i];
$j++;
}else{$j = 0; $k++;}
}
About matrix multiplication, I found this interesting post: http://sickel.net/blogg/?p=907
Using the exact function found there:
function matrixmult($m1,$m2){
$r=count($m1);
$c=count($m2[0]);
$p=count($m2);
if(count($m1[0])!=$p){throw new Exception('Incompatible matrixes');}
$m3=array();
for ($i=0;$i< $r;$i++){
for($j=0;$j<$c;$j++){
$m3[$i][$j]=0;
for($k=0;$k<$p;$k++){
$m3[$i][$j]+=$m1[$i][$k]*$m2[$k][$j];
}
}
}
return($m3);
}
$c = matrixmult($data2, $data2);
foreach($c as $k => $v){
$i = 0;
foreach($v as $kk => $vv){
echo $vv . ' | ';
$i++;
if($i == count($v))
echo '<br/>';
}
}
The result is quite close to the needed pattern:
2.9999 | 1.6 | 8.5 |
5.6665 | 3 | 16 |
1.0666 | 0.56665 | 2.9999 |
The slight difference comes from the rounding method. If that's an issue, see round() function.
sqrt() function is for calculating square root. If you want to loop all elements in array then remove sqrt() call.
In PHP, array usually zero-based index, so you need to replace $i=1, $j=1 to start from 0 in your for loop.
See Matrix multiplication

PHP - Use everything from 1st array, remove duplicates from 2nd?

I have 2 arrays - the first one is output first in full. The 2nd one may have some values that were already used/output with the first array. I want to "clean up" the 2nd array so that I can output its data without worrying about showing duplicates. Just to be sure I have the terminology right & don't have some sort of "array within an array", this is how I access each one:
1st Array
$firstResponse = $sth->fetchAll();
foreach ($firstResponse as $firstResponseItem) {
echo $firstResponseItem['samecolumnname']; // Don't care if it's in 2nd array
}
2nd Array
while( $secondResponseRow = $dbRequest->fetch_assoc() ){
$secondResponseArray = array($secondResponseRow);
foreach ($secondResponseArray as $secondResponseItem){
echo $secondResponseItem['samecolumnname']; //This can't match anything above
}
}
Thanks!
For example:
$response_names = array();
$firstResponse = $sth->fetchAll();
foreach ($firstResponse as $firstResponseItem)
$response_names[] = $firstResponseItem['samecolumnname'];
while( $secondResponseRow = $dbRequest->fetch_assoc() ){
$secondResponseArray = array($secondResponseRow);
foreach ($secondResponseArray as $secondResponseItem) {
if (!in_array($secondResponseItem['samecolumnname'], $response_names))
$response_names[] = $secondResponseItem['samecolumnname'];
}
}
array_walk($response_names, function($value) { echo $value . '<br />' });
If I understand what you're looking to do and the arrays are in the same scope, this should work.
$secondResponseArray = array($secondResponseRow);
$secondResponseArray = array_diff($secondResponseArray, $firstResponse);
//secondResponseArray now contains only unique items
foreach ($secondResponseArray as $secondResponseItem){
echo $secondResponseItem['samecolumnname'];
}
If you know that the keys of duplicate values will be the same you could use array_diff_assoc to get the items of the first array that aren't in the other supplied arrays.
This code
<?php
$a = array('abc', 'def', 'ghi', 'adg');
$b = array('abc', 'hfs', 'toast', 'adgi');
$r = array_diff_assoc($b, $a);
print_r($a);
print_r($r);
produces the following output
[kernel#~]php so_1.php
Array
(
[0] => abc
[1] => def
[2] => ghi
[3] => adg
)
Array
(
[1] => hfs
[2] => toast
[3] => adgi
)
[kernel#~]

How do I compare a huge amount of strings against the beginning of another?

I have two tables. One with a load of numbers. I then have another table with a list of prefixes( 30, 000+ ).
I need to loop through the prefixes and see if any of the numbers in table 1 starts with any of the prefixes.
This is what I have so far.
$tdata = $r->get_t_data(); //array of prefix
/*
Array
(
[0] => Array
(
[prefix] => 101
[dest] => UK
)
)
*/
$cdata = $r->get_c_data(); //array of number
/*Array
(
[0] => Array
(
[row] => 1
[num] => 441143610120
)
)*/
$temp = array();
$i=0;
$time=0;
foreach ($cdata as $ckey => $c) {
foreach ($tdata as $tkey => $t) {
$length = strlen($t['prefix']);
if (strpos($c['num'], $t['prefix'], 0, $length )) {
$temp[$i]['row']=$c['row'];
$temp[$i]['prefix']=$t['prefix'];
$temp[$i]['dialled']=$c['num'];
$temp[$i]['dest']=$t['dest'];
break;
$i++; //increment only if found
}
}
$time++;
}
so basically it loops through the numbers and then I try and match the first part of the number with the prefix.
At the moment it is returning and empty array.
Hope you can help
The best thing to do is to do the join in your sql as opposed to checking after in your PHP. To do a join with a like you can do this:
SELECT * FROM table t JOIN prefixTable p ON t.num LIKE CONCAT(p.prefix, '%')
The key is LIKE CONCAT(p.prefix, '%') that's saying combine the tables where t.num is like prefix%and in MySQL % is a wildcard since we didn't put a wild card at the front that means that the t.num column has to START with prefix
Your condition if (strpos($c['num'], $t['prefix'], 0, $length )) can return 0, which php will interpret as false. strpos should be checked like this:
if (false !== strpos($c['num'], $t['prefix'], 0, $length )) {}
use preg_grep to reduce the amount of looping/search code you have:
foreach ($table1 as $search) {
$safe_search = preg_quote($search);
$matches = preg_grep("/^$safe_search/", $prefix_array);
if (count($matches) > 0) {
echo "Found $search in the prefix array\n";
}
}

Nested foreach with arrays - php

The problem I am facing is when I use one foreach inside another and the array of the first foreach has more than 1 entries. What I want to do is to exclude all entries of array 1 from array 2. I've been on almost all related posts, cannot solve it by myself, I need a little help if possible. Sorry for my bad English.
Example:
$choice ---> array with random number of entries each time (for this example 2)
Example:
/var/www/clients/client1/web1/web/images,/var/www/clients/client1/web1/web/tmp
$list ---> array of random number of entries each time (for this example 10000)
Example:
/var/www/clients/client1/web1/web/images,/var/www/clients/client1/web1/web/tmp,/var/www/clients/client1/web1/web/includes,/var/www/clients/client1/web1/web/libraries,......
$list has always more entries than $choice
And I have this code here:
foreach ( $choice as $select )
{
foreach ( $list as $file )
{
if ( (strpos( $file, $select )) !== false )
{
// exclude this
}
else
{
// include this
}
}
}
What the above code will do (unfortunately) is:
Step 1. Will compare $select entry-1 with all $file entries.
Step 2. Will exclude $select entry-1 from all $file entries and will include the $select entry-2.
Step 3. Will compare $select entry-2 with all $file entries.
Step 4. Will exclude $select entry-2 from all $file entries and will include the $select entry-1.
Result:
Nothing excluded.
Any help truly appreciated. I am on this for like a week, all I have tried is putting them inside out I am out of ideas.
Thank you.
I believe you're trying to remove items that are in $list from $choice. (Or is it the other way around?) Have you tried the array_diff function? This will work if items in both array are equal. For example:
<?php
//Option 1: array_diff
$bigger = array("A", "B", "C", "D", "E", "F");
$smaller = array("A", "B");
$result = array_diff($bigger, $smaller);
print_r($result);
If you need to do additional processing on the removed items, you can try in_array, but this requires item equality (like above). For example:
//Option 2: in_array (only one foreach loop)
foreach ($smaller as $key => $item) {
if (in_array($item, $bigger)) {
//do something to "remove" it, for example:
unset($smaller[$key]);
unset($bigger[$key]);
//...
}
}
print_r($smaller);
print_r($bigger);
Lastly, if the items in both arrays are not guaranteed to be strictly equals, you could use a double foreach. You'll need to flag items in the inner loop and process them in the outer loop. For example:
//Option 3: double-foreach (items not strictly equals)
$choice = array(
"/var/www/clients/client1/web1/web/images",
"/var/www/clients/client1/web1/web/tmp"
);
$list = array(
"/var/www/clients/client1/web1/web/images",
"/var/www/clients/client1/web1/web/tmp",
"/var/www/clients/client1/web1/web/includes",
"/var/www/clients/client1/web1/web/libraries",
// more items
);
foreach ($choice as $choice_key => $choice_item) {
$exists_in_list = FALSE;
foreach ($list as $list_key => $list_item) {
if (strpos($list_item, $choice_item) !== FALSE) {
//$choice_item is string-contained inside $list_item:
$exists_in_list = TRUE;
//Do some processing on $list (while "$list_key" is in scope). For example:
unset($list[$list_key]); //removes the matching items from $list
//...
break;
}
}
if ($exists_in_list) {
//Do post-processing on $choice. For example:
unset($choice[$choice_key]); //removes the matching items from $choice
//...
}
}
echo '$choice is now ';
print_r($choice);
echo '$list is now ';
print_r($list);
The $result is:
//Option 1:
Array //$result == $bigger - $smaller
(
[2] => C
[3] => D
[4] => E
[5] => F
)
//Option 2:
Array //$smaller - $bigger
(
)
Array //$bigger - $smaller
(
[2] => C
[3] => D
[4] => E
[5] => F
)
//Option 3:
$choice is now Array
(
)
$list is now Array
(
[2] => /var/www/clients/client1/web1/web/includes
[3] => /var/www/clients/client1/web1/web/libraries
)

Categories