Finding similar strings in array - php

I need to harness similar_text() for an array of values that look something like this:
$strings = ["lawyer" => 3, "business" => 3, "lawyers" => 1, "a" => 3];
What I'm trying to do is find the words what are practically the same, i.e. lawyer and lawyers in the above array, and add the counts for them together in a new array.
So lawyer would be 4 as lawyers would be associated to the original string of lawyer.
Keep in mind, this array will only ever be singular words and the length is unspecified, it could range from 1 to >99.
I had no idea where to start with this, so I gave it a crack with a foreach loop as you'll see below, but the intended output isn't as expected.
foreach ( $strings as $key_one => $count_one ) {
foreach ( $strings as $key_two => $count_two ) {
similar_text($key_two, $key_one, $percent);
if ($percent > 80) {
if(!isset($counts[$key_one])) {
$counts[$key_one] = $count_one;
} else {
$counts[$key_one] += $count_two;
}
}
}
}
Note: The percent match is at 80 for this example (as the match for lawyer & lawyers is ~92%)
Which ends up giving me something similar to the following:
Array
(
[lawyer] => 4
[business] => 3
[a] => 3
[lawyers] => 2
)
Where I require it to be:
Array
(
[lawyer] => 4
[business] => 3
[a] => 3
)
Notice how i require it to practically remove lawyers and add the count to lawyer.

Your difficulty is that just as lawyer is similar to lawyers, lawyers is also similar to lawyer. So they both get their count bumped up by the other.
Try this:
foreach ( $strings as $key_one => &$count_one ) {
if ($count_one == 0) continue; // skip it if we've already processed it
if (!isset($counts[$key_one]) {
$counts[$key_one] = $count_one;
$count_one = 0;
}
foreach ( $strings as $key_two => &$count_two ) {
similar_text($key_two, $key_one, $percent);
if ($percent > 80) {
$counts[$key_one] += $count_two;
$count_two = 0;
}
}
}
The disadvantage of that is that you change your original $strings array which may not be ideal. Here's another approach, keeping track of already-processed strings in another hash:
$already = $counts = array(); // not really necessary, but nice to init
foreach ( $strings as $key_one => $count_one ) {
if (isset($already[$key_one])) continue; // skip if already processed
$counts[$key_one] = $count_one; // by definition this should be new
foreach ( $strings as $key_two => $count_two ) {
similar_text($key_two, $key_one, $percent);
if ($percent > 80) {
$counts[$key_one] += $count_two;
$already[$key_two] = true;
}
}
}
I would recommend the 2nd solution.

You can always use
unset( $counts[$key_two] ) ;

Related

How to check if two strings have the same beginning?

Considering I have one-dimensional ordered array of strings:
$arr = [
'Something else',
'This is option: one',
'This is option: two',
'This is option: 😜',
'This is second option: 2',
'This is second option: 3'
];
I would like to turn it into two-dimensional array, having the common beginning as the key. For example:
$target = [
'Something else',
'This is option:' => [
'one',
'two',
'😜'
],
'This is second option:' => [
'2',
'3'
]
];
It sounds simple, but I have gone completely blank.
function convertArr(array $src): array {
$prevString = null;
$newArray = [];
foreach ($src as $string) {
if ($prevString) {
// stuck here
}
$prevString = $string;
}
return $newArray;
}
Pre-made fiddle: https://3v4l.org/eqGDc
How can I check if two strings start with the same words, without having to loop on each letter?
As of now I have written this overly-complicated function, but I wonder if there is a simpler way:
function convertArr(array $src): array {
$prevString = null;
$newArray = [];
$size = count($src);
for ($i = 0; $i < $size; $i++) {
if (!$prevString || strpos($src[$i], $prevString) !== 0) {
if ($i == $size - 1) {
$newArray[] = $src[$i];
break;
}
$nowWords = explode(' ', $src[$i]);
$nextWords = explode(' ', $src[$i + 1]);
foreach ($nowWords as $k => $v) {
if ($v != $nextWords[$k]) {
break;
}
}
if ($k) {
$prevString = implode(' ', array_splice($nowWords, 0, $k));
$newArray[$prevString][] = implode(' ', $nowWords);
}
} else {
$newArray[$prevString][] = trim(substr($src[$i], strlen($prevString)));
}
}
return $newArray;
}
This might do the job:
function convertArray(array $values): array
{
$newArray = [];
foreach ($values as $value) {
if (stripos($value, ':') !== false) {
$key = strtok($value, ':');
$newArray[$key][] = trim(substr($value, stripos($value, ':') + 1));
}
}
return $newArray;
}
Essentially, based on the format of your array of strings, as long as each string only has one ":" character followed by the option value, this should work well enough.
I'm sure there will be a more advanced and more fail-safe solution but this may be a start.
I haven't got a complete solution, but maybe you can use this as a starting point: The following gets you the longest common starting sequence for the strings in an array of length 2:
var s=["This is option: one","This is option: two"];
var same=s.join('|').match(/(.*)(.*?)\|\1.*/)[1];
// same="This is option: "
In same you will find the longest possible beginning of the two strings in array s. I achieve this by using a regular expression with a greedy and a non-greedy wildcard group and forcing the first group to be repeated.
You could apply this method on slice()-d short arrays of your original sorted input array and monitor whether same stays the same for a number of these sub-arrays. You can then perform your intended grouping operation on sections with the same same.
[[ Sorry, I just realized I coded this in JavaScript and you wanted PHP - but the idea is so simple you can translate that easily into PHP yourself. ]]
Edit
When looking at the question and expected result again it seems to me, that what the OP really wants is to combine elements with similar parts before the colon (:) into a common sub-array. This can be done with the following code:
$arr = [
'Is there anything new and',
'Something old',
'This is option: one',
'This is option: two',
'This is option: 😜',
'This is second option: 2',
'This is second option: 3',
'Abc: def',
'Abc: ghi',
'the same line',
'the same words'
];
foreach($arr as $v) {
$t=array_reverse(explode(':',$v));
$target[isset($t[1])?trim($t[1]):0][]=trim($t[0]);
}
print_r($target)
output:
Array
(
[0] => Array
(
[0] => Is there anything new and
[1] => Something old
[2] => the same line
[3] => the same words
)
[This is option] => Array
(
[0] => one
[1] => two
[2] => 😜
)
[This is second option] => Array
(
[0] => 2
[1] => 3
)
[Abc] => Array
(
[0] => def
[1] => ghi
)
)
See a demo here https://rextester.com/JMB6676

PHP multidimensional array not giving output

I've tried to display this information tons of times, i've looked all over stackoverflow and just can't find an answer, this isn't a duplicate question, none of the solutions on here work. I've a json array which is stored as a string in a database, when it's taken from the database it's put into an array using json_decode and looks like this
Array
(
[0] => Array
(
[0] => Array
(
)
[1] => Array
(
[CanViewAdminCP] => Array
(
[Type] => System
[Description] => Grants user access to view specific page
[Colour] => blue
)
)
)
)
However, when i try to loop through this, it just returns nothing, I've tried looping using keys, i've tried foreach loops, nothing is returning the values, I'm looking to get the Array key so "CanViewAdminCP" and then the values inside that key such as "Type" and "Description".
Please can anybody help? thankyou.
Use a recursive function to search for the target key CanViewAdminCP recursively, as follows:
function find_value_by_key($haystack, $target_key)
{
$return = false;
foreach ($haystack as $key => $value)
{
if ($key === $target_key) {
return $value;
}
if (is_array($value)) {
$return = find_value_by_key($value, $target_key);
}
}
return $return;
}
Example:
print_r(find_value_by_key($data, 'CanViewAdminCP'));
Array
(
[Type] => System
[Description] => Grants user access to view specific page
[Colour] => blue
)
Visit this link to test it.
You have a 4 level multidimensional array (an array containing an array containing an array containing an array), so you will need four nested loops if you want to iterate over all keys/values.
This will output "System" directly:
<?php echo $myArray[0][1]['CanViewAdminCP']['Type']; ?>
[0] fetches the first entry of the top level array
[1] fetches the second entry of that array
['CanViewAdminCP'] fetches that keyed value of the third level array
['Type'] then fetches that keyed value of the fourth level array
Try this nested loop to understand how nested arrays work:
foreach($myArray as $k1=>$v1){
echo "Key level 1: ".$k1."\n";
foreach($v1 as $k2=>$v2){
echo "Key level 2: ".$k2."\n";
foreach($v2 as $k3=>$v3){
echo "Key level 3: ".$k3."\n";
}
}
}
Please consider following code which will not continue after finding the first occurrence of the key, unlike in Tommassos answer.
<?php
$yourArray =
array(
array(
array(),
array(
'CanViewAdminCP' => array(
'Type' => 'System',
'Description' => 'Grants user access to view specific page',
'Colour' => 'blue'
)
),
array(),
array(),
array()
)
);
$total_cycles = 0;
$count = 0;
$found = 0;
function searchKeyInMultiArray($array, $key) {
global $count, $found, $total_cycles;
$total_cycles++;
$count++;
if( isset($array[$key]) ) {
$found = $count;
return $array[$key];
} else {
foreach($array as $elem) {
if(is_array($elem))
$return = searchKeyInMultiArray($elem, $key);
if(!is_null($return)) break;
}
}
$count--;
return $return;
}
$myDesiredArray = searchKeyInMultiArray($yourArray, 'CanViewAdminCP');
print_r($myDesiredArray);
echo "<br>found in depth ".$found." and traversed ".$total_cycles." arrays";
?>

Get most deeply nested arrays

I have heterogenous nested arrays (each contains a mix of scalars and arrays, which also may contain scalars and arrays, and so on recursively). The goal is to extract all arrays with the maximum depth. Note this does not mean extracting arrays at the "bottom" of any given sub-array (local maximums), but the greatest depth over all sub-arrays.
For example:
$testArray= array(
'test1' => 'SingleValue1',
'test2' => 'SingleValue2',
'test3' => array(0,1,2),
'test4' => array(array(3,4,array(5,6,7)), array(8,9,array(10,11,12)),13,14),
'test5' => array(15,16,17, array(18,19,20)),
);
In this example, the greatest depth any array occurs at is 3, and there are two arrays at that depth:
array(5,6,7)
array(10,11,12)
The code should find these two. (The [18,19,20] sub-array is not included, for though it's at the greatest depth in its branch, it's at a lesser depth overall.)
I'm not sure where to start. I've tried many things: using foreach in recursive functions, etc., but the end result was always nothing, all elements or the last iterated element. How can this problem be approached? Complete solutions aren't needed, just hints on where to start.
Extended solution with RecursiveIteratorIterator class:
$testArray= array(
'test1' => 'SingleValue1',
'test2' => 'SingleValue2',
'test3' => array(0,1,2),
'test4' => array(array(3,4,array(5,6,7)), array(8,9,array(10,11,12)),13,14),
'test5' => array(15,16,17, array(18,19,20)),
);
$it = new \RecursiveArrayIterator($testArray);
$it = new \RecursiveIteratorIterator($it, \RecursiveIteratorIterator::CHILD_FIRST);
$max_depth = 0;
$items = $deepmost = [];
foreach ($it as $item) {
$depth = $it->getDepth(); // current subiterator depth
if ($depth > $max_depth) { // determining max depth
$max_depth = $depth;
$items = [];
}
if (is_array($item)) {
$items[$depth][] = $item;
}
}
if ($items) {
$max_key = max(array_keys($items)); // get max key pointing to the max depth
$deepmost = $items[$max_key];
unset($items);
}
print_r($deepmost);
The output:
Array
(
[0] => Array
(
[0] => 5
[1] => 6
[2] => 7
)
[1] => Array
(
[0] => 10
[1] => 11
[2] => 12
)
)
You may wrap this approach into a named function and use it for getting the deepmost arrays.
Enjoy! )
Roman's solution seems to work, but I struggle to read that type of method. Here's my version of finding the deepest subarrays.
See my inline comments for explanation of each step. Basically, it checks each array for subarrays, then iterates/recurses when possible, and storea subarrays using the level counter as a key.
My custom function will return an array of arrays.
Code: (Multidimensional Array Demo) (Flat Array Demo) (Empty Array Demo)
function deepestArrays(array $array, int $level = 0, array &$lowest = []): array
{
$subarrays = array_filter($array, 'is_array');
if ($subarrays) { // a deeper level exists
foreach ($subarrays as $subarray) {
deepestArrays($subarray, $level + 1, $lowest); // recurse each subarray
}
} else { // deepest level in branch
$lowestLevel = key($lowest) ?? $level; // if lowest array is empty, key will be null, fallback to $level value
if ($lowestLevel === $level) {
$lowest[$level][] = $array; // push the array into the results
} elseif ($lowestLevel < $level) {
$lowest = [$level => [$array]]; // overwrite with new lowest array
}
}
return current($lowest); // return the deepest array
}
var_export(
deepestArrays($testArray)
);

Get the first key for an element in PHP?

I'm trying to add an extra class tag if an element / value is the first one found in an array. The problem is, I don't really know what the key will be...
A minified example of the array
Array (
[0] => Array(
id => 1
name = Miller
)
[1] => Array(
id => 4
name = Miller
)
[2] => Array(
id => 2
name => Smith
[3] => Array(
id => 7
name => Jones
)
[4] => Array(
id => 9
name => Smith
)
)
So, if it's the first instance of "name", then I want to add a class.
I think I sort of understand what you're trying to do. You could loop through the array and check each name. Keep the names you've already created a class for in a separate array.
for each element in this array
if is in array 'done already' then: continue
else:
create the new class
add name to the 'done already' array
Sorry for the pseudo-code, but it explains it fairly well.
Edit: here's the code for it...
$done = array();
foreach ($array as $item) {
if (in_array($item['name'], $done)) continue;
// It's the first, do something
$done[] = $item['name'];
}
I assume you're looping through this array. So, a simple condition can be used:
$first = 0;
foreach ($arr as $value) {
if (!$first++) echo "first class!";
echo $value;
// The rest of process.
}
To get just the first value of an array, you can also use the old fashioned reset() and current() functions:
reset($arr);
$first = current($arr);
Animuson has it good. There a small addition if I might:
> $done = array();
> foreach ($array as $item) {
> if (in_array($item['name'], $done)) continue;
> // It's the first, do something
> $done[] = $item['name']; }
the in_array() will get slow very fast. Try something like:
$done = array();
foreach ($array as $item) {
$key = $item['name'];
if( !isset( $done[$key] )) {
...
}
$done[$key] = true;
}
btw. Too bad this site doesn't support comments for all users.

Replace keys in an array based on another lookup/mapping array

I have an associative array in the form key => value where key is a numerical value, however it is not a sequential numerical value. The key is actually an ID number and the value is a count. This is fine for most instances, however I want a function that gets the human-readable name of the array and uses that for the key, without changing the value.
I didn't see a function that does this, but I'm assuming I need to provide the old key and new key (both of which I have) and transform the array. Is there an efficient way of doing this?
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
The way you would do this and preserve the ordering of the array is by putting the array keys into a separate array, find and replace the key in that array and then combine it back with the values.
Here is a function that does just that:
function change_key( $array, $old_key, $new_key ) {
if( ! array_key_exists( $old_key, $array ) )
return $array;
$keys = array_keys( $array );
$keys[ array_search( $old_key, $keys ) ] = $new_key;
return array_combine( $keys, $array );
}
if your array is built from a database query, you can change the key directly from the mysql statement:
instead of
"select ´id´ from ´tablename´..."
use something like:
"select ´id´ **as NEWNAME** from ´tablename´..."
The answer from KernelM is nice, but in order to avoid the issue raised by Greg in the comment (conflicting keys), using a new array would be safer
$newarr[$newkey] = $oldarr[$oldkey];
$oldarr=$newarr;
unset($newarr);
$array = [
'old1' => 1
'old2' => 2
];
$renameMap = [
'old1' => 'new1',
'old2' => 'new2'
];
$array = array_combine(array_map(function($el) use ($renameMap) {
return $renameMap[$el];
}, array_keys($array)), array_values($array));
/*
$array = [
'new1' => 1
'new2' => 2
];
*/
You could use a second associative array that maps human readable names to the id's. That would also provide a Many to 1 relationship. Then do something like this:
echo 'Widgets: ' . $data[$humanreadbleMapping['Widgets']];
If you want also the position of the new array key to be the same as the old one you can do this:
function change_array_key( $array, $old_key, $new_key) {
if(!is_array($array)){ print 'You must enter a array as a haystack!'; exit; }
if(!array_key_exists($old_key, $array)){
return $array;
}
$key_pos = array_search($old_key, array_keys($array));
$arr_before = array_slice($array, 0, $key_pos);
$arr_after = array_slice($array, $key_pos + 1);
$arr_renamed = array($new_key => $array[$old_key]);
return $arr_before + $arr_renamed + $arr_after;
}
Simple benchmark comparison of both solution.
Solution 1 Copy and remove (order lost, but way faster) https://stackoverflow.com/a/240676/1617857
<?php
$array = ['test' => 'value', ['etc...']];
$array['test2'] = $array['test'];
unset($array['test']);
Solution 2 Rename the key https://stackoverflow.com/a/21299719/1617857
<?php
$array = ['test' => 'value', ['etc...']];
$keys = array_keys( $array );
$keys[array_search('test', $keys, true)] = 'test2';
array_combine( $keys, $array );
Benchmark:
<?php
$array = ['test' => 'value', ['etc...']];
for ($i =0; $i < 100000000; $i++){
// Solution 1
}
for ($i =0; $i < 100000000; $i++){
// Solution 2
}
Results:
php solution1.php 6.33s user 0.02s system 99% cpu 6.356 total
php solution1.php 6.37s user 0.01s system 99% cpu 6.390 total
php solution2.php 12.14s user 0.01s system 99% cpu 12.164 total
php solution2.php 12.57s user 0.03s system 99% cpu 12.612 total
If your array is recursive you can use this function:
test this data:
$datos = array
(
'0' => array
(
'no' => 1,
'id_maquina' => 1,
'id_transaccion' => 1276316093,
'ultimo_cambio' => 'asdfsaf',
'fecha_ultimo_mantenimiento' => 1275804000,
'mecanico_ultimo_mantenimiento' =>'asdfas',
'fecha_ultima_reparacion' => 1275804000,
'mecanico_ultima_reparacion' => 'sadfasf',
'fecha_siguiente_mantenimiento' => 1275804000,
'fecha_ultima_falla' => 0,
'total_fallas' => 0,
),
'1' => array
(
'no' => 2,
'id_maquina' => 2,
'id_transaccion' => 1276494575,
'ultimo_cambio' => 'xx',
'fecha_ultimo_mantenimiento' => 1275372000,
'mecanico_ultimo_mantenimiento' => 'xx',
'fecha_ultima_reparacion' => 1275458400,
'mecanico_ultima_reparacion' => 'xx',
'fecha_siguiente_mantenimiento' => 1275372000,
'fecha_ultima_falla' => 0,
'total_fallas' => 0,
)
);
here is the function:
function changekeyname($array, $newkey, $oldkey)
{
foreach ($array as $key => $value)
{
if (is_array($value))
$array[$key] = changekeyname($value,$newkey,$oldkey);
else
{
$array[$newkey] = $array[$oldkey];
}
}
unset($array[$oldkey]);
return $array;
}
I like KernelM's solution, but I needed something that would handle potential key conflicts (where a new key may match an existing key). Here is what I came up with:
function swapKeys( &$arr, $origKey, $newKey, &$pendingKeys ) {
if( !isset( $arr[$newKey] ) ) {
$arr[$newKey] = $arr[$origKey];
unset( $arr[$origKey] );
if( isset( $pendingKeys[$origKey] ) ) {
// recursion to handle conflicting keys with conflicting keys
swapKeys( $arr, $pendingKeys[$origKey], $origKey, $pendingKeys );
unset( $pendingKeys[$origKey] );
}
} elseif( $newKey != $origKey ) {
$pendingKeys[$newKey] = $origKey;
}
}
You can then cycle through an array like this:
$myArray = array( '1970-01-01 00:00:01', '1970-01-01 00:01:00' );
$pendingKeys = array();
foreach( $myArray as $key => $myArrayValue ) {
// NOTE: strtotime( '1970-01-01 00:00:01' ) = 1 (a conflicting key)
$timestamp = strtotime( $myArrayValue );
swapKeys( $myArray, $key, $timestamp, $pendingKeys );
}
// RESULT: $myArray == array( 1=>'1970-01-01 00:00:01', 60=>'1970-01-01 00:01:00' )
Here is a helper function to achieve that:
/**
* Helper function to rename array keys.
*/
function _rename_arr_key($oldkey, $newkey, array &$arr) {
if (array_key_exists($oldkey, $arr)) {
$arr[$newkey] = $arr[$oldkey];
unset($arr[$oldkey]);
return TRUE;
} else {
return FALSE;
}
}
pretty based on #KernelM answer.
Usage:
_rename_arr_key('oldkey', 'newkey', $my_array);
It will return true on successful rename, otherwise false.
this code will help to change the oldkey to new one
$i = 0;
$keys_array=array("0"=>"one","1"=>"two");
$keys = array_keys($keys_array);
for($i=0;$i<count($keys);$i++) {
$keys_array[$keys_array[$i]]=$keys_array[$i];
unset($keys_array[$i]);
}
print_r($keys_array);
display like
$keys_array=array("one"=>"one","two"=>"two");
Easy stuff:
this function will accept the target $hash and $replacements is also a hash containing newkey=>oldkey associations.
This function will preserve original order, but could be problematic for very large (like above 10k records) arrays regarding performance & memory.
function keyRename(array $hash, array $replacements) {
$new=array();
foreach($hash as $k=>$v)
{
if($ok=array_search($k,$replacements))
$k=$ok;
$new[$k]=$v;
}
return $new;
}
this alternative function would do the same, with far better performance & memory usage, at the cost of losing original order (which should not be a problem since it is hashtable!)
function keyRename(array $hash, array $replacements) {
foreach($hash as $k=>$v)
if($ok=array_search($k,$replacements))
{
$hash[$ok]=$v;
unset($hash[$k]);
}
return $hash;
}
This page has been peppered with a wide interpretation of what is required because there is no minimal, verifiable example in the question body. Some answers are merely trying to solve the "title" without bothering to understand the question requirements.
The key is actually an ID number and the value is a count. This is
fine for most instances, however I want a function that gets the
human-readable name of the array and uses that for the key, without
changing the value.
PHP keys cannot be changed but they can be replaced -- this is why so many answers are advising the use of array_search() (a relatively poor performer) and unset().
Ultimately, you want to create a new array with names as keys relating to the original count. This is most efficiently done via a lookup array because searching for keys will always outperform searching for values.
Code: (Demo)
$idCounts = [
3 => 15,
7 => 12,
8 => 10,
9 => 4
];
$idNames = [
1 => 'Steve',
2 => 'Georgia',
3 => 'Elon',
4 => 'Fiona',
5 => 'Tim',
6 => 'Petra',
7 => 'Quentin',
8 => 'Raymond',
9 => 'Barb'
];
$result = [];
foreach ($idCounts as $id => $count) {
if (isset($idNames[$id])) {
$result[$idNames[$id]] = $count;
}
}
var_export($result);
Output:
array (
'Elon' => 15,
'Quentin' => 12,
'Raymond' => 10,
'Barb' => 4,
)
This technique maintains the original array order (in case the sorting matters), doesn't do any unnecessary iterating, and will be very swift because of isset().
If you want to replace several keys at once (preserving order):
/**
* Rename keys of an array
* #param array $array (asoc)
* #param array $replacement_keys (indexed)
* #return array
*/
function rename_keys($array, $replacement_keys) {
return array_combine($replacement_keys, array_values($array));
}
Usage:
$myarr = array("a" => 22, "b" => 144, "c" => 43);
$newkeys = array("x","y","z");
print_r(rename_keys($myarr, $newkeys));
//must return: array("x" => 22, "y" => 144, "z" => 43);
You can use this function based on array_walk:
function mapToIDs($array, $id_field_name = 'id')
{
$result = [];
array_walk($array,
function(&$value, $key) use (&$result, $id_field_name)
{
$result[$value[$id_field_name]] = $value;
}
);
return $result;
}
$arr = [0 => ['id' => 'one', 'fruit' => 'apple'], 1 => ['id' => 'two', 'fruit' => 'banana']];
print_r($arr);
print_r(mapToIDs($arr));
It gives:
Array(
[0] => Array(
[id] => one
[fruit] => apple
)
[1] => Array(
[id] => two
[fruit] => banana
)
)
Array(
[one] => Array(
[id] => one
[fruit] => apple
)
[two] => Array(
[id] => two
[fruit] => banana
)
)
This basic function handles swapping array keys and keeping the array in the original order...
public function keySwap(array $resource, array $keys)
{
$newResource = [];
foreach($resource as $k => $r){
if(array_key_exists($k,$keys)){
$newResource[$keys[$k]] = $r;
}else{
$newResource[$k] = $r;
}
}
return $newResource;
}
You could then loop through and swap all 'a' keys with 'z' for example...
$inputs = [
0 => ['a'=>'1','b'=>'2'],
1 => ['a'=>'3','b'=>'4']
]
$keySwap = ['a'=>'z'];
foreach($inputs as $k=>$i){
$inputs[$k] = $this->keySwap($i,$keySwap);
}
This function will rename an array key, keeping its position, by combining with index searching.
function renameArrKey($arr, $oldKey, $newKey){
if(!isset($arr[$oldKey])) return $arr; // Failsafe
$keys = array_keys($arr);
$keys[array_search($oldKey, $keys)] = $newKey;
$newArr = array_combine($keys, $arr);
return $newArr;
}
Usage:
$arr = renameArrKey($arr, 'old_key', 'new_key');
this works for renaming the first key:
$a = ['catine' => 'cat', 'canine' => 'dog'];
$tmpa['feline'] = $a['catine'];
unset($a['catine']);
$a = $tmpa + $a;
then, print_r($a) renders a repaired in-order array:
Array
(
[feline] => cat
[canine] => dog
)
this works for renaming an arbitrary key:
$a = ['canine' => 'dog', 'catine' => 'cat', 'porcine' => 'pig']
$af = array_flip($a)
$af['cat'] = 'feline';
$a = array_flip($af)
print_r($a)
Array
(
[canine] => dog
[feline] => cat
[porcine] => pig
)
a generalized function:
function renameKey($oldkey, $newkey, $array) {
$val = $array[$oldkey];
$tmp_A = array_flip($array);
$tmp_A[$val] = $newkey;
return array_flip($tmp_A);
}
There is an alternative way to change the key of an array element when working with a full array - without changing the order of the array.
It's simply to copy the array into a new array.
For instance, I was working with a mixed, multi-dimensional array that contained indexed and associative keys - and I wanted to replace the integer keys with their values, without breaking the order.
I did so by switching key/value for all numeric array entries - here: ['0'=>'foo']. Note that the order is intact.
<?php
$arr = [
'foo',
'bar'=>'alfa',
'baz'=>['a'=>'hello', 'b'=>'world'],
];
foreach($arr as $k=>$v) {
$kk = is_numeric($k) ? $v : $k;
$vv = is_numeric($k) ? null : $v;
$arr2[$kk] = $vv;
}
print_r($arr2);
Output:
Array (
[foo] =>
[bar] => alfa
[baz] => Array (
[a] => hello
[b] => world
)
)
best way is using reference, and not using unset (which make another step to clean memory)
$tab = ['two' => [] ];
solution:
$tab['newname'] = & $tab['two'];
you have one original and one reference with new name.
or if you don't want have two names in one value is good make another tab and foreach on reference
foreach($tab as $key=> & $value) {
if($key=='two') {
$newtab["newname"] = & $tab[$key];
} else {
$newtab[$key] = & $tab[$key];
}
}
Iterration is better on keys than clone all array, and cleaning old array if you have long data like 100 rows +++ etc..
One which preservers ordering that's simple to understand:
function rename_array_key(array $array, $old_key, $new_key) {
if (!array_key_exists($old_key, $array)) {
return $array;
}
$new_array = [];
foreach ($array as $key => $value) {
$new_key = $old_key === $key
? $new_key
: $key;
$new_array[$new_key] = $value;
}
return $new_array;
}
Here is an experiment (test)
Initial array (keys like 0,1,2)
$some_array[] = '6110';//
$some_array[] = '6111';//
$some_array[] = '6210';//
I must change key names to for example human_readable15, human_readable16, human_readable17
Something similar as already posted. During each loop i set necessary key name and remove corresponding key from the initial array.
For example, i inserted into mysql $some_array got lastInsertId and i need to send key-value pair back to jquery.
$first_id_of_inserted = 7;//lastInsertId
$last_loop_for_some_array = count($some_array);
for ($current_loop = 0; $current_loop < $last_loop_for_some_array ; $current_loop++) {
$some_array['human_readable'.($first_id_of_inserted + $current_loop)] = $some_array[$current_loop];//add new key for intial array
unset( $some_array[$current_loop] );//remove already renamed key from array
}
And here is the new array with renamed keys
echo '<pre>', print_r($some_array, true), '</pre>$some_array in '. basename(__FILE__, '.php'). '.php <br/>';
If instead of human_readable15, human_readable16, human_readable17 need something other. Then could create something like this
$arr_with_key_names[] = 'human_readable';
$arr_with_key_names[] = 'something_another';
$arr_with_key_names[] = 'and_something_else';
for ($current_loop = 0; $current_loop < $last_loop_for_some_array ; $current_loop++) {
$some_array[$arr_with_key_names[$current_loop]] = $some_array[$current_loop];//add new key for intial array
unset( $some_array[$current_loop] );//remove already renamed key from array
}
Hmm, I'm not test before, but I think this code working
function replace_array_key($data) {
$mapping = [
'old_key_1' => 'new_key_1',
'old_key_2' => 'new_key_2',
];
$data = json_encode($data);
foreach ($mapping as $needed => $replace) {
$data = str_replace('"'.$needed.'":', '"'.$replace.'":', $data);
}
return json_decode($data, true);
}
You can write simple function that applies the callback to the keys of the given array. Similar to array_map
<?php
function array_map_keys(callable $callback, array $array) {
return array_merge([], ...array_map(
function ($key, $value) use ($callback) { return [$callback($key) => $value]; },
array_keys($array),
$array
));
}
$array = ['a' => 1, 'b' => 'test', 'c' => ['x' => 1, 'y' => 2]];
$newArray = array_map_keys(function($key) { return 'new' . ucfirst($key); }, $array);
echo json_encode($array); // {"a":1,"b":"test","c":{"x":1,"y":2}}
echo json_encode($newArray); // {"newA":1,"newB":"test","newC":{"x":1,"y":2}}
Here is a gist https://gist.github.com/vardius/650367e15abfb58bcd72ca47eff096ca#file-array_map_keys-php.

Categories