Writing a cloning function to generate unique names - php

I would like to create a clone function on the following array,
$usernames = array ( 'jack', 'amy', 'chris');
such that:
Cloning jack, would result in jack-1 (because jack-1 does not exist in usernames array yet). Once cloned, the usernames array should be updated to:
$usernames = array ( 'jack', 'amy', 'chris', 'jack-1');
Cloning jack, (again) would result in jack-2
Cloning jack-1, would result in jack-1-1
Cloning jack-1-1, would result in jack-1-1-1
Cloning jack-1-1 (again), would result in jack-1-1-2
Cloning jack-1-1-1 would result in jack-1-1-1-1
and so on and so forth..
I can work with in_array to do this, but looking for an efficient way to do this.
Thanks,

I've tested this and it works.
First, we check if the the value already exists, if it doesn't, we just go ahead and add it.
If it does exist, we add the incrementing number with a dash to it. We keep incrementing until we get to one that doesn't exist...then we add it.
<?php
$array = ['jack', 'sally'];
function cloneFunction($value, $array)
{
if (!in_array($value, $array))
{
$array[] = $value;
}
else
{
$i = 0;
while(in_array($value, $array))
{
$i++;
$value = $value . '-' . $i;
}
$array[] = $value;
}
return $array;
// Do return $value if you just want the value.
}
print_r(cloneFunction('jack', $array));

Based on Sajan's Logic, it works according to the requirement, if I modify the logic this way. Copying the original username and re-parsing the original array, does the trick.
$array = array('jack', 'sally');
function cloneFunction($value, $array)
{
if (!in_array($value, $array))
{
$array[] = $value;
}
else
{
$i = 0;
$j = 0;
while(in_array($value, $array))
{
$i++;
$value = $value . '-' . $i;
$stagedValue = $value;
while(in_array($value, $array))
{
$j++;
$value = $stagedValue . '-' . $j;
}
$j = $i;
}
$array[] = $value;
}
return $array;
// Do return $value if you just want the value.
}
print_r(cloneFunction('jack', $array));

Related

Optimize multidimensional array creation

I have a list of array keys and nesting level in one array, for example:
$keys[0] = 'first';
$keys[1] = 'second';
$keys[2] = 'third';
How can I transform this into a multidimensional array in the following format:
$array['first']['second']['third'] = 'value';
I tried a couple different variations that did not work out and the keys overwrote themselves. The simplest way is to get a count of the number of keys and manually cover each scenario but this is hardly optimized and does not dynamically grow.
$keyLen = count($keys);
if ($keyLen == 1) {
$array[$keys[0]] = 'value;
} elseif ($keyLen == 2) {
$array[$keys[0]][$keys[1]] = 'value;
} elseif ($keyLen == 3) {
$array[$keys[0]][$keys[1]][$keys[2]] = 'value';
} ...
A couple notes, the value is not of significance it's the nesting of the array keys, and I cannot change the initial array format.
You could go backwards through your input array and wrap the previous result as you go:
$nested = 'value';
for (end($keys); key($keys)!==null; prev($keys)){
$nested = [current($keys) => $nested];
}
At the end $nested will have the desired structure.
Give this a try. Here we're using references to stack our new arrays.
<?php
$keys = array('first', 'second', 'third');
$array = array();
$current = &$array;
foreach($keys as $key => $value) {
$current[$value] = array();
$current = &$current[$value];
}
$current = 'Hello world!';
print_r($array);
Starting from the last Item you can loop through the array, pop the last one and build the new array:
<?php
$keys[0] = 'first';
$keys[1] = 'second';
$keys[2] = 'third';
function createKey($array) {
$b = "value";
while(count($array)>0) {
$key = array_pop($array);
$b = [$key => $b];
}
return $b;
}
var_dump(createKey($keys));
// Output:
array(1) {
["first"]=>
array(1) {
["second"]=>
array(1) {
["third"]=>
string(5) "value"
}
}
}
Another option is to loop the reversed keys and wrap the previous result:
$array = [];
foreach(array_reverse($keys) as $key) {
$temp = $array;
unset($array[key($array)]);
$array[$key] = $temp;
}
Then you could set your value:
$array['first']['second']['third'] = 'value';
Demo
You could even eval this in a safe way without leaking any input data into the eval-string:
$value = 'value';
eval( '$array[$keys['. implode(']][$keys[', range(0, count($keys)-1)) . ']] = $value;' );
or with an additional counter even simpler and shorter:
$value = 'value';
$i = 0;
eval( '$array' . str_repeat('[$keys[$i++]]', count($keys)) . ' = $value;' );

PHP: Check whether array_filter deleted any entry from the array

is there any way to check whether array_filter deleted any value from an array?
The solutions I've thought of are these ones:
$sample_array = ["values"];
$array_copy = $sample_array;
if (array_filter($sample_array, 'fn') === $array_copy) {
#Some value was deleted from the array
} else {
#The array was not modified
}
This one doesn't seem very efficient, as it has to copy the entire array.
$sample_array = ["values"];
$array_count = count($sample_array);
if (count(array_filter($sample_array, 'fn')) === $array_count) {
#The array was not modified
} else {
#Some value was deleted from the array
}
This one seems more efficient, though I was wondering if there was a more elegant way of approaching the issue.
Thank you beforehand.
You can extend your function to keep track of the change itself.
$array = [1, 2, 3, 4];
$changed = 0;
$new = array_filter($array, function($e) use (&$changed) {
$result = $e <= 2;
if(!$result) $changed++;
return $result;
});
Returns $changed as 2.
Use array_diff to compare two arrays.
if(array_diff($array_copy, $sample_array))
echo "not same";
Using 'count()' is IMHO the most elegant solution and, I suspect, may be a lot more efficient - you'd need to measure it. OTOH, for your consideration...
function fn($arg, $mode='default')
{
static $matched;
if ('default'==$mode) {
$result=real_filter_fn($arg);
$matched+=$result ? 1 : 0;
return $result;
}
$result=$matched;
$matched=0;
return $result;
}
If you want to keep track of the removed elements, you could do something like this.
$sample_array = ['values', 'test'];
$removed = []; // Contains all the removed values.
$new_array = array_filter($sample_array, function($value) use (&$removed) {
if($value == 'test') {
$removed[] = $value; // Adds the value to the removed array.
return false;
}
return true;
});
print_r($removed);
You could use a closure to change the state of a boolean variable:
$sample_array = [1,2,3,4];
$array_changed = false;
//Use closure to change $array_changed
print_r(array_filter($sample_array, function($var) use (&$array_changed) {
$check = (!($var & 1));
$array_changed = $array_changed?:$check;
return $check;
}));
if ($array_changed) {
#Some value was deleted from the array
} else {
#The array was not modified
}
[EDIT]
This is a simple speedtest of your solutions compared to mine:
/**********************SPEEDTEST**************************/
function fn ($var) {
return (!($var & 1));
}
$sample_array = [1,2,3,4];
$before = microtime(true);
for ($i=0 ; $i<100000 ; $i++) {
$array_changed = false;
//Use closure to change $array_changed
array_filter($sample_array, function($var) use (&$array_changed) {
$check = (!($var & 1));
$array_changed = $array_changed?:$check;
return $check;
});
if ($array_changed) {
#Some value was deleted from the array
} else {
#The array was not modified
}
}
$after = microtime(true);
echo "Using closure:      " . ($after-$before)/$i . " sec/run\n";
echo '<br>';
/*******************************************************/
$before = microtime(true);
for ($i=0 ; $i<100000 ; $i++) {
$array_copy = $sample_array;
if (array_filter($sample_array, 'fn') === $array_copy) {
#Some value was deleted from the array
} else {
#The array was not modified
}
}
$after = microtime(true);
echo "Using array copy:    " . ($after-$before)/$i . " sec/run\n";
echo '<br>';
/*******************************************************/
$before = microtime(true);
for ($i=0 ; $i<100000 ; $i++) {
$array_count = count($sample_array);
if (count(array_filter($sample_array, 'fn')) === $array_count) {
#The array was not modified
} else {
#Some value was deleted from the array
}
}
$after = microtime(true);
echo "Using array count:   " . ($after-$before)/$i . " sec/run\n";
The results:
Using closure: 9.1402053833008E-7 sec/run
Using array copy: 4.9885988235474E-7 sec/run
Using array count: 5.5881977081299E-7 sec/run
Maybe that helps with decision-making :)

How to recursively combine array in php

I want to combine two arrays into a dictionary.
The keys will be the distinct values of the first array, the values will be all values from the second array, at matching index positions of the key.
<?php
$a=[2,3,4,5,6,7,8,9,10];
$b=[1,1,3,2,1,2,6,8,8];
?>
array_combine($b,$a);
Expected result as
<?php
/*
Value '1' occurs at index 0, 1 and 4 in $b
Those indices map to values 2, 3 and 6 in $a
*/
$result=[1=>[2,3,6],3=>4,2=>[5,7],6=>8,8=>[9,10]];
?>
There are quite a few PHP array functions. I'm not aware of one that solves your specific problem. you might be able to use some combination of built in php array functions but it might take you a while to weed through your choices and put them together in the correct way. I would just write my own function.
Something like this:
function myCustomArrayFormatter($array1, $array2) {
$result = array();
$num_occurrences = array_count_values($array1);
foreach ($array1 AS $key => $var) {
if ($num_occurrences[$var] > 1) {
$result[$var][] = $array2[$key];
} else {
$result[$var] = $array2[$key];
}
}
return $result;
}
hope that helps.
$a=[2,3,4,5,6,7,8,9,10];
$b=[1,1,3,2,1,2,6,8,8];
$results = array();
for ($x = 0; $x < count($b); $x++) {
$index = $b[$x];
if(array_key_exists ($index, $results)){
$temp = $results[$index];
}else{
$temp = array();
}
$temp[] = $a[$x];
$results[$index] = $temp;
}
print_r($results);
Here's one way to do this:
$res = [];
foreach ($b as $b_index => $b_val) {
if (!empty($res[$b_val])) {
if (is_array($res[$b_val])) {
$res[$b_val][] = $a[$b_index];
} else {
$res[$b_val] = [$res[$b_val], $a[$b_index]];
}
} else {
$res[$b_val] = $a[$b_index];
}
}
var_dump($res);
UPDATE: another way to do this:
$val_to_index = array_combine($a, $b);
$result = [];
foreach ($val_to_index as $value => $index) {
if(empty($result[$index])){
$result[$index] = $value;
} else if(is_array($result[$index])){
$result[$index][] = $value;
} else {
$result[$index] = [$result[$index], $value];
}
}
var_dump($result);

Issue with custom script to sort Arrays ascending and descending order

I have an issue to deal with here (a logical error in my code 99%). I just can't seem to find the way to fix it, but I bet one of you will find the problem in no time!
I have to create a function which sorts array passed to it in asc or desc order, but can't use any array sorting functions !
I've been struggling with loops until now and I finally want to ask help from other devs ( you ).
Currently only code for ascending is worked on, descending will be no problem I assume once I do this one. It kinda of does sort values up to some point, but then stops ( it stops if the next smallest value is at the end of the passed array ). What could I do to prevent this and make it sort the whole array and it's elements?
Here is the code so far.
<?php
function order_array($array,$mode = 'ascending') {
$length = count($array);
if($mode == 'descending') {
return $array;
} else {
$sorted_array = array();
$used_indexes = array();
for($i = 0; $i < $length; $i++) {
$smallest = true;
echo $array[$i] . '<br/>';
for($y = 0; $y < $length; $y++) {
//echo $array[$i] . ' > ' . $array[$y] . '<br/>';
// if at ANY time during checking element vs other ones in his array, he is BIGGER than that element
// set smallest to false
if(!in_array($y,$used_indexes)) {
if($array[$i] > $array[$y]) {
$smallest = false;
break;
}
}
}
if($smallest) {
$sorted_array[] = $array[$i];
$used_indexes[] = $i;
}
}
return $sorted_array;
}
}
$array_to_sort = array(1, 3, 100, 99, 33, 20);
$sorted_array = order_array($array_to_sort);
print_r($sorted_array);
?>
I've solved the issue myself by doing it completely different. Now it sorts correctly all the elements of the passed in array. The logical issue I had was of using for() loop. The for() loop ran only a set ( length of passed array ) number of times, while we need it to loop more than that, because we will need to loop all the way untill we have a new sorted array in ascending order. Here is the code that will work
function order_array($array,$mode = 'ascending') {
if($mode == 'descending') {
// for() wont work here, since it will only loop an array length of times, when we would need it
// to loop more than that.
while(count($array)){
$value = MAX($array);
$key = array_search($value, $array);
if ($key !== false) {
unset($array[$key]);
}
$sorted[] = $value;
}
return $sorted;
} else {
// for() wont work here, since it will only loop an array length of times, when we would need it
// to loop more than that.
while(count($array)){
$value = MIN($array);
$key = array_search($value, $array);
if ($key !== false) {
unset($array[$key]);
}
$sorted[] = $value;
}
return $sorted;
}
}
function order_array($array,$mode = 'ascending') {
$length = count($array);
$sorted_array = array();
$used_indexes = array();
for($i = 0; $i < $length; $i++) {
$smallest = true;
echo $array[$i] . '<br/>';
for($y = 0; $y < $length; $y++) {
//echo $array[$i] . ' > ' . $array[$y] . '<br/>';
// if at ANY time during checking element vs other ones in his array, he is BIGGER than that element
// set smallest to false
if(!in_array($y,$used_indexes)) {
if($array[$i] > $array[$y]) {
$smallest = false;
break;
}
}
}
if($smallest) {
$sorted_array[] = $array[$i];
$used_indexes[] = $i;
}
if($mode == 'descending') {
return array_reverse($sorted_array);
}
return $sorted_array;
}
}

Determining duplicate letter counts between an array of strings

I have an array of strings of random letters, and I need to know which letters are consistent between the array members. The count of the letters are important.
My method right now is loop through the array, doing a split, then looping through the spitted string to count the occurrences of each letter, then update the array with letter => count
Then do an array_reduce that creates a new array of members who only occur in all arrays. But, it's not working.
<?
$a[] = "emaijuqqrauw";
$a[] = "aaeggimqruuz";
$a[] = "aabimqrtuuzw";
$a[] = "aacikmqruuxz";
$a[] = "aacikmqruuxz";
$a[] = "aaciimqruuxy";
foreach($a as $b){
$n = str_split($b, 1);
foreach($n as $z){
$arr[$z] = substr_count($b, $z);
}
ksort($arr);
$array[] = $arr;
unset($arr);
}
$n = array_reduce($array, function($result, $item){
if($result === null){
return $item;
}else{
foreach($item as $key => $val){
if(isset($result[$key])){
$new[$key] = $val;
}
}
return $new;
}
});
foreach($n as $key => $val){
echo str_repeat($key, $val);
}
This returns aaiimqruu - which is kinda right, but there's only 2 i's in the last element of the array. There's only one i in the rest. I'm not sure how to break that down farther and get it to return aaimqruu- which I'll then pop into a SQL query to find a matching word, aquarium
There's array_intersect(), which is most likely what you'd want. Given your $a array, you'd do something like:
$a = array(.... your array...);
$cnt = count($a);
for($i = 0; $i < $cnt; $i++) {
$a[$i] = explode('', $a[$i]); // split each string into array of letters
}
$common = $a[0]; // save the first element
for($i = 1; $i < $cnt; $i++) {
$common = array_intersect($common, $a[$i]);
}
var_dump($common);
How about you do it this way? Finds out the occurrence of an item throughout the array.
function findDuplicate($string, $array) {
$count = 0;
foreach($array as $item) {
$pieces = str_split($item);
$pcount= array_count_values($pieces);
if(isset($pcount[$string])) {
$count += $pcount[$string];
}
}
return $count;
}
echo findDuplicate("a",$a);
Tested :)
Gives 12, using your array, which is correct.
Update
My solution above already had your answer
$pieces = str_split($item);
$pcount= array_count_values($pieces);
//$pcount contains, every count like [a] => 2
Seems like array_reduce is the best function for what this purpose, however I just didn't think of adding a conditional to give me the desired effect.
$new[$key] = ($result[$key] > $val) ? $val : $result[$key];
to replace
$new[$key] = $val;
did the trick.

Categories