Replace IDs inside array with PHP - php

I have an array like this:
Array
(
[1] => Array
(
[uris] => Array
(
[1] => /
[2] => /news/
[3] => /about/
[4] => /contact/
)
[templates] => Array
(
[1] => 1
[2] => 2
[3] => 3
[4] => 4
)
)
)
and I need to update all the IDs with a new set of values so the final output should be something like:
[uris] => Array
(
[100] => /
[101] => /news/
[102] => /about/
[103] => /contact/
)
[templates] => Array
(
[100] => 1
[101] => 2
[102] => 3
[103] => 4
)
I think I might need to use a regex pattern like '/(\[.*?\])/' but I'm not sure how to put it together to get the final updated output.
EDIT: I should mention that the IDs I'm updating to won't be in sequential order, they're coming from a database table of entries in a CMS. Entries are being duplicated so they'll have the same URIs but the second set with have a higher set of IDs than the original ones.
There won't be a rule for the second set of IDs. The only guarantee is that the second set will all be higher than the highest ID from the first set.
The CMS I'm working with allows me to duplicate entries for use on a second site. But the CMS also uses a third party module that creates a serialized array like I've posted above which matches URIs against templates. The module doesn't create a new serialized array of the new entry IDs when the entries get duplicated, so what's I'm trying to do manually.
I thought I could unserialize the array, update the string's old IDs with the corresponding IDs of the new entries, reserialize it, and then copy it back into the database.

Array (
[1] => /
[2] => /news/
[3] => /about/
[4] => /contact/
)
Given this kind of array, what you need to do to replace the keys is simply:
$array = array_combine(range(100, 100 + (count($array) - 1)), $array);
It generates keys [100, .., 103] and splices them into the array.

You might have to loop and then replace the keys :
foreach ($yourArray as $array) {
for ($i = 0; $i < count($array['uris']); $i++) {
$array['uris'][$i][$newkey] = $array['uris'][$i][$oldkey];
unset($array['uris'][$i][$oldkey]);
}
for ($i = 0; $i < count($array['templates']); $i++) {
$array['templates'][$i][$newkey] = $array['templates'][$i][$oldkey];
unset($array['templates'][$i][$oldkey]);
}
}

As per your question update; not too sure what you're trying to do, but I have two guesses.
arr2d_replace_id will take all the uris and increase key and also a new set of array with updated keys and it's value.
arr2d_replace_id2 will 1) flip array of templates - where I'm guessing the value represents the ID of route in uris and so flipped to preserve values; 2) increase key value on uris; 3) change flipped array's value according to the key based on uris; 4) flip the array back with the new set info and assign to result.
function arr2d_replace_id($arr, $inc) {
$result = array();
foreach ($arr['uris'] as $k=>$v) {
$result['uris'][intval($k) + $inc] = $v;
$result['templates'][intval($k) + $inc] = $arr['templates'][$k];
}
return $result;
}
function arr2d_replace_id2($arr, $inc) {
$result = array();
$tpl_flipped = array_flip($arr['templates']);
foreach ($arr['uris'] as $k=>$v) {
$result['uris'][intval($k) + $inc] = $v;
$tpl_flipped[$k] = intval($k) + $inc;
}
$result['templates'] = array_flip($tpl_flipped);
return $result;
}
$test = array(
'uris' => array(
1 => '/', '/news/', '/about/', '/contact/',
),
'templates' => array(
1 => 1, 2, 3, 4
)
);
print_r( $test );
print_r( arr2d_replace_id($test, 100) );
print_r( arr2d_replace_id2($test, 100) );
See in action: http://3v4l.org/T6hHD

If the array values are unique, you can use array_flip:
$array = [
1 => '/',
2 => '/contact/',
3 => '/about/'
];
$counter = 100;
$array = array_flip($array);
$array = array_map(function($oldKey) use (&$counter) {
return ++$counter;
}, $array);
$array = array_flip($array);

$mainArray = array();
$mainArray[]=array("uris" => array("/", '/news', '/about', '/contact') , "templates" => array(1,2,3,4));
foreach ($mainArray as &$array) {
$uris = $array['uris'];
$keys = array_keys($uris);
for($i=0;$i<count($keys);$i++)
{
$uris[$keys[$i] + 100]=$uris[$i];
unset($uris[$i]);
}
$template = $array['templates'];
$keys = array_keys($uris);
for($i=0;$i<count($keys);$i++)
{
$template[$keys[$i] + 100]=$template[$i];
unset($template[$i]);
}
$array['uris'] = $uris;
$array['templates'] = $template;
}
echo "<pre>";
print_r($mainArray);
exit;

Try this one,
$array = array(
array('uris' => array(
'1' => '/',
'2' => '/news/',
'3' => '/about/',
'4' => '/contact/'
),
'templates' => array
(
'1' => '1',
'2' => '2',
'3' => '3',
'4' => '4'
)
)
);
$val = 100;
$result = array();
foreach ($array as $key => $value) {
foreach ($value as $key1 => $value1) {
foreach ($value1 as $key2 => $value2) {
$result[$key][$key1][$val] = $value2;
$val++;
}
}
}
var_dump($result);

Related

Group data by leading portion of keys and push values into each group as indexed elements

I have the following working code that from 2 separate arrays ($I & $f) creates final multidimension array with the data as associated columns.
The problem is i feel the code is clunky, but i cant see if, or how it could me improved. So I'm hoping a second pair of eyes can help.
<?php
//main array of input data
$i = [ 'input_tickettype1_storeno_00' => null,
'input_tickettype1_deliverydate_00' => null,
'input_tickettype1_ticketref_00' => null,
'input_tickettype1_storeno_01' => '9874',
'input_tickettype1_deliverydate_01' => '2022-02-01',
'input_tickettype1_ticketref_01' => 'EDN6547',
'input_tickettype1_storeno_02' => '8547',
'input_tickettype1_deliverydate_02' => '2022-01-31',
'input_tickettype1_ticketref_02' => 'EDN5473',
'input_tickettype1_storeno_03' => '9214',
'input_tickettype1_deliverydate_03' => '2022-02-28',
'input_tickettype1_ticketref_03' => 'EDN1073'
];
//headers
$h = [ 'input_tickettype1_storeno' ,
'input_tickettype1_deliverydate',
'input_tickettype1_ticketref'
];
//final multidim array
$f = array();
//Create a multidim for the headers and the values
foreach ($h as $k => $v)
{
$f[] = [$v=>null];
}
//loop throught the headers looping for matches in the input data
for ($x = 0; $x < count($f); $x++) {
foreach ($f[$x] as $fk => $fv) {
foreach ($i as $ik => $iv) {
if (str_contains($ik,$fk)) {
array_push($f[$x],$iv);
}
}
}
}
print_r($f);
//Actual Working Output
// Array (
// [0] => Array ( [input_tickettype1_storeno] =>
// [0] =>
// [1] => 9874
// [2] => 8547
// [3] => 9214
// )
// [1] => Array ( [input_tickettype1_deliverydate] =>
// [0] =>
// [1] => 2022-02-01
// [2] => 2022-01-31
// [3] => 2022-02-28
// )
// [2] => Array ( [input_tickettype1_ticketref] =>
// [0] =>
// [1] => EDN6547
// [2] => EDN5473
// [3] => EDN1073
// )
// )
?>
Yes, indeed I think the code can be optimised for readability and logic.
I can think of two methods you can use.
Method 1 : nested foreach
First of all, you don't need a foreach to initialize your multidimentional array, you can do it within the main loop you use to read the data. So you can remove the foreach -- $f[] = [$v=>null];
Then, instead of having 1 for and 2 foreach you can just have 2 foreach one for each array and use a very fast strpos to identify if the key matches and populate the final array.
Here's the resulting code.
$f = [];
foreach ($h as $prefix) {
$f[$prefix] = [];
foreach ($i as $key => $val) {
if (strpos($key, $prefix) === 0) {
$f[$prefix][] = $val;
}
}
}
This first method is simple, with a straightforward logic. However it requires a nested foreach. Which means that if both arrays get larger, your code gets much slower.
Method 2 : key manipulation
This method assumes that the keys of the first array never change structure and they are always somestringid_[digits]
In this case we can avoid looping the second array and just use a regular expression to recreate the key.
$f = [];
foreach ($i as $key => $value) {
preg_match('/^(.*)_[0-9]+$/', $key, $m);
$key = $m[1];
if (empty($f[$m[1]])) {
$f[$m[1]] = [];
}
$f[$m[1]][] = $value;
}
I don't see any need to implement any additional data or conditions. You only need to read your main array, mutate the keys (trim the trailing unique identifiers) as you iterate, and push data into their relative groups.
Code: (Demo)
$result = [];
foreach ($array as $k => $v) {
$result[preg_replace('/_\d+$/', '', $k)][] = $v;
}
var_export($result);
Output:
array (
'input_tickettype1_storeno' =>
array (
0 => NULL,
1 => '9874',
2 => '8547',
3 => '9214',
),
'input_tickettype1_deliverydate' =>
array (
0 => NULL,
1 => '2022-02-01',
2 => '2022-01-31',
3 => '2022-02-28',
),
'input_tickettype1_ticketref' =>
array (
0 => NULL,
1 => 'EDN6547',
2 => 'EDN5473',
3 => 'EDN1073',
),
)

Storing value in unknown depth of multidimensional array

I have this exath path saved somewhere:
Array
(
[0] => library
[1] => 1
[2] => book
[3] => 0
[4] => title
[5] => 1
)
I have some array and I want to change the value on this index:
$values[library][1][book][0][title][1] = "new value";
I have no idea, how to do this, because there can be any (unknown) number of dimensions. Any hints?
It makes sense to create a function that does this, so:
function array_path_set(array & $array, array $path, $newValue) {
$aux =& $array;
foreach ($path as $key) {
if (isset($aux[$key])) {
$aux =& $aux[$key];
} else {
return false;
}
}
$aux = $newValue;
return true;
}
$values = array(
'library' => array(
1 => array(
'book' => array(
0 => array(
'title' => array(
1 => 'MAGIC VALUE!',
),
),
),
),
),
);
$path = array('library', 1, 'book', 0, 'title', 1);
$newValue = 'ANOTHER MAGIC VALUE!';
var_dump($values);
var_dump(array_path_set($values, $path, $newValue));
var_dump($values);
Try
foreach ($array as $val) {
$indexes .= "[$val]";
}
${'output'.$indexes} = 'something';
Or
$indexes = '';
foreach ($array as $val) {
$indexes .= "[$val]";
}
$output = 'values'.$indexes;
$$output = 'something';
Are you trying to provide a new value for the title of book 0 in library 1? If so, you will have to search for library "1", with book "0" and then change the value of the "title". So if all your values in the array have the same six entries, start with loc = 0 look at the values at loc+1 (library id), loc+3 (book id) and loc+5 .. change title) .. if not increment loc by 6 and continue searching.
[sounds like homework, so no code provided. Pardon me if I am wrong.]
Just in case you weren't aware of it, the key
$values["library"][1]["book"][0]["title"][1]
Is not the same as the array example in your post. Presuming the array is $values, it has five elements:
$values = Array
(
[0] => "library"
[1] => 1
[2] => "book"
[3] => 0
[4] => "title"
[5] => 1
)
$values[0] = "Library";
$values[1] = "1";
$values[2] = "book";
$values[3] = "0";
etc...
Is this array structure what you intended? If not, post back with a more complete structure so we can help.
Also, you need quotes around the strings - I have included for clarity
You could take a look at the following link for array_search. Some of the posters have included examples of a multidimensional array search and there are other examples. Do a google search on "multidimensional array search" and you will likely find a solution. If you need more direction, post back your details.
try this ->
$keys = array('0'=>'for','1'=>'test','2'=>'only');
$value='ok';
function addArrayPathWithValue($keys,$value,$array = array(),$current =
array())
{
$function = __FUNCTION__;
if (count($current)==0)
{
$keys = array_reverse($keys);
$current = $value;
}
if (count($keys)==0)
{
return $current;
}
$array[array_shift($keys)]=$current;
return $function($keys,$value,NULL,$array);
}
$array = addArrayPathWithValue($keys,$value);
print_r($array);
//output: Array ( [for] => Array ( [test] => Array ( [only] => ok ) ) )

How to group the result of array in two categories in PHP?

I have data here:
Array
(
[1] => Array
(
[SiteID] => 1
[OwnerAID] => 1
)
[3] => Array
(
[SiteID] => 3
[OwnerAID] => 1
)
[6] => Array
(
[SiteID] => 6
[OwnerAID] => 2
)
)
Now, I need to group the OwnerAID into two categories: the first one is OwnerAID owning only one SiteID and the second one is OwnerAID owning more than 1 SiteID.
I've tried to make a program and do some research, but the output of my code is wrong.
Please see my code:
public function groupIndividualAndAggregateSites()
{
$arrays = array();
foreach($this->combined as $key => $key_value)
{
$SiteID = "";
if ($SiteID == "") {
$SiteID=array($key_value['SiteID']); }
else {
$SiteID = array_merge((array)$SiteID, (array)$key_value['SiteID']);
$SiteID = array_unique($SiteID);
}
} if ($SiteID != "") {
$arrays = array('AID'=>$key_value['AID'], 'Sites' => $SiteID);
}
print_r($arrays);
}
The result should be shown like this:
Array(
[1] => Array
( [Sites] => Array ([0] => 1, [1] => 3)))
Array(
[2] => Array
( [Sites] => Array ([0] => [6]))
What you should go for is array:
$owners = array(
owner_1 => SiteID1, // int Only one site
owner_2 => array (SiteID2,SiteID3), // array Multiple sites
);
and later use the array $owners like:
echo (is_array($owners[$owner_1]) ? 'Has multiple sites' : 'has one site';
Thats the basic idea of small memory footprint.
Example, not tested.
public function groupIndividualAndAggregateSites() {
$owners = array();
foreach($this->combined as $key => $value) {
$owner_id = $value['OwnerAID'];
$site_id = $value['SiteID'];
if(array_key_exists($owner_id,$owners)) {
// He has one or more sites already?
if(is_array($owners[$owner_id]){
array_push($owners[$owner_id],$site_id);
} else {
// User already has one site. Lets make an array instead and add old and new siteID
$old_site_id = $owners[$owner_id];
$owners[$owner_id] = array($old_site_id,$owner_id);
}
} else {
$owners[$owner_id] = $site_id;
}
return $owners;
}
All you need to do is loop over your initial array, appending each OwnerAID to the relevant output subarray as determined by the SiteID:
$output = array(1=>array(), 2=>array());
foreach ($original as $v) $output[$v['SiteID'] == 1 ? 1 : 2][] = $v['OwnerAID'];
Here I am using the following features:
array() initialisation function;
foreach control structure;
ternary operator;
$array[$key] addressing;
$array[] pushing.

Group array by keys value

I have searched around and I am at my wits end so I will make this easy. This is what I have in a print_r:
Array
(
[0] => Array
(
[name] => client interaction
[y] => 2.7
)
[1] => Array
(
[name] => client interaction
[y] => 0.1
)
[2] => Array
(
[name] => project planning
[y] => 0.1
)
[3] => Array
(
[name] => client interaction
[y] => 5.9
)
)
And this is what I want it to be:
Array
(
[0] => Array
(
[name] => client interaction
[y] => 8.7
)
[1] => Array
(
[name] => project planning
[y] => 0.1
)
)
Is it absolutely necessary that your desired array use numeric indeces? If I were to do this I would do it this way (not the same as your desired array though)
$newArray = array();
foreach($array as $item)
{
if(!isset($newArray[$item["name"]]))
$newArray[$item["name"]] = 0;
$newArray[$item["name"]] += $item["y"];
}
This will give you an array of this structure:
Array
(
[client interaction] => 8.7
[project planning] => 0.1
)
To get the keys back you simply use the second form of the foreach loop
foreach($newArray as $name => $y)
$name will contain the name and $y the y in your original array.
$hash = array();
foreach ($sourceArray as $key=>$value) {
$y = 0;
if (isset($hash{$value{'name'}})) {
$y = $hash{$value{'name'}};
}
$hash{$value{'name'}} = $y + $value{'y'};
}
$result = array();
foreach ($hash as $key => $value) {
$result[] = array( 'name' => $key, 'value' => $value );
}
print_r($result);
The last loop is just to get $hash into the exact format you specified.
Explanation:
$hash
Is a temporary structure used to compute the sums for each unique name.
foreach ($sourceArray as $key=>$value) {
This goes through your original array ($sourceArray) and pulls out each element.
$y = 0;
Initializes a variable to store the current sum that belongs with that name.
if (isset($hash{$value{'name'}})) {
Checks to see if a value has already been stored in $hash for the given name.
$y = $hash{$value{'name'}};
}
Sets $y to the previously calculated sum for the given name, if there was one.
$hash{$value{'name'}} = $y + $value{'y'};
}
Stores the sum for the given name into our temporary structure $hash.
$result = array();
foreach ($hash as $key => $value) {
$result[] = array( 'name' => $key, 'value' => $value );
}
converts $hash to the format you desired.
The empy []'s in $result[] = ... add the right hand side of the expression to the end of the $result array.
$mixed = array(); // Should contain your array shown above
$groups = array();
$newArray = array(); // Will contain your new array
$counter = 0;
foreach( $mixed as $mix )
{
if ( isset($groups[$mix['name']]) )
{
$newArray[$groups[$mix['name']]]['y'] += $mix['y'];
}
else
{
$groups[$mix['name']] = $counter;
$newArray[] = $mix;
$counter++;
}
}
http://codepad.org/EjdHxgvt

parsing out the last number of the post

Ok so i have a post that looks kind of this
[optional_premium_1] => Array
(
[0] => 61
)
[optional_premium_2] => Array
(
[0] => 55
)
[optional_premium_3] => Array
(
[0] => 55
)
[premium_1] => Array
(
[0] => 33
)
[premium_2] => Array
(
[0] => 36 )
[premium_3] => Array
(
[0] => 88 )
[premium_4] => Array
(
[0] => 51
)
how do i get the highest number out of the that. So for example, the optional "optional_premium_" highest is 3 and the "premium_" optional the highest is 4. How do i find the highest in this $_POST
You could use array_key_exists(), perhaps something like this:
function getHighest($variableNamePrefix, array $arrayToCheck) {
$continue = true;
$highest = 0;
while($continue) {
if (!array_key_exists($variableNamePrefix . "_" . ($highest + 1) , $arrayToCheck)) {
$continue = false;
} else {
highest++;
}
}
//If 0 is returned than nothing was set for $variableNamePrefix
return $highest;
}
$highestOptionalPremium = getHighest('optional_premium', $_POST);
$highestPremium = getHighest('premium', $_POST);
I have 1 question with 2 parts before I answer, and that is why are you using embedded arrays? Your post would be much simpler if you used a standard notation like:
$_POST['form_input_name'] = 'whatever';
unless you are specifically building this post with arrays for some reason. That way you could use the array key as the variable name and the array value normally.
So given:
$arr = array(
"optional_premium_1" => "61"
"optional_premium_2" => "55"
);
you could use
$key = array_keys($arr);
//gets the keys for that array
//then loop through get raw values
foreach($key as $val){
str_replace("optional_premium_", '', $val);
}
//then loop through again to compare each one
$highest = 0;
for each($key as $val){
if ((int)$val > $highest) $highest = (int)$val;
}
that should get you the highest one, but then you have to go back and compare them to do whatever your end plan for it was.
You could also break those into 2 separate arrays and assuming they are added in order just use end() http://php.net/manual/en/function.end.php
Loop through all POST array elements, pick out elements having key names matching "name_number" pattern and save the ones having the largest number portion of the key names. Here is a PHP script which does it:
<?php // test.php 20110428_0900
// Build temporary array to simulate $_POST
$TEMP_POST = array(
"optional_premium_1" => array(61),
"optional_premium_2" => array(55),
"optional_premium_3" => array(55),
"premium_1" => array(33),
"premium_2" => array(36),
"premium_3" => array(88),
"premium_4" => array(51),
);
$names = array(); // Array of POST variable names
// loop through all POST array elements
foreach ($TEMP_POST as $k => $v) {
// Process only elements with names matching "word_number" pattern.
if (preg_match('/^(\w+)_(\d+)$/', $k, $m)) {
$name = $m[1];
$number = (int)$m[2];
if (!isset($names[$name]))
{ // Add new POST var key name to names array
$names[$name] = array(
"name" => $name,
"max_num" => $number,
"key_name" => $k,
"value" => $v,
);
} elseif ($number > $names[$name]['max_num'])
{ // New largest number in key name.
$names[$name] = array(
"name" => $name,
"max_num" => $number,
"key_name" => $k,
"value" => $v,
);
}
}
}
print_r($names);
?>
Here is the output from the script:
Array
(
[optional_premium] => Array
(
[name] => optional_premium
[max_num] => 3
[key_name] => optional_premium_3
[value] => Array
(
[0] => 55
)
)
[premium] => Array
(
[name] => premium
[max_num] => 4
[key_name] => premium_4
[value] => Array
(
[0] => 51
)
)
)
Though ineffective, you could try something like
$largest = 0;
foreach($_POST as $key => $value)
{
$len = strlen("optional_premium_");
$num = substr($key, $len);
if($num > $largest)
$largest = $num;
}
print_r($largest);
The problem being that this will only work for one set of categories. It will most likely give errors throughout the script.
Ideally, you would want to reorganize your post, make each array be something like
[optional_premium_1] => Array
(
[0] => 61
["key"] => 1
)
[optional_premium_2] => Array
(
[0] => 61
["key"] => 2
)
Then just foreach and use $array["key"] to search.

Categories