I'm trying to loop through one array, adding a new level to another array each time. Let me illustrate - variable $arr's values are different each time
$arr = array("1","5","6");
Looping
$index[$arr[0]];
Looping
$index["1"][$arr[1]] // "1" since this key was filled in by the previous loop, continuing with a new key
Looping
$index["1"]["5"][$arr[2]] // same as previous loop
--looped over all $arr's items, done, result is $index["1"]["5"]["6"]--
The problem is I won't know how much values the $arr array contains. Then, I don't know how to continue from, for example, $index["1"] when the first value of $arr has been looped to the next array level (other words: add another key)..
Anyone?
You can use references here:
$a = array("1","5","6");
$b = array();
$c =& $b;
foreach ($a as $k) {
$c[$k] = array();
$c =& $c[$k];
}
outputs
Array
(
[1] => Array
(
[5] => Array
(
[6] => Array
(
)
)
)
)
To overwrite the last element with some other value, you can just add the line:
$c = 'blubber';
after the loop, because $c is a reference to the deepest array level, when the loop is finished.
function add_inner_array(&$array, $index) {
if(isset($array[$index])) return true;
else {
$array[$index] = array();
return true;
}
}
$a = array(1,5,6);
$index = array();
$pass =& $index;
foreach($a as $k) {
add_inner_array($pass, $k);
$pass =& $pass[$k];
}
Related
I am trying to sort it in a repeating, sequential pattern of numerical order with the largest sets first.
Sample array:
$array = [1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3];
In the above array, I have the highest value of 5 which appears twice so the first two sets would 1,2,3,4,5 then it would revert to the second, highest value set etc.
Desired result:
[1,2,3,4,5,1,2,3,4,5,1,2,3,4,1,2,4]
I am pretty sure I can split the array into chunks of the integer values then cherrypick an item from each subarray sequentially until there are no remaining items, but I just feel that this is going to be poor for performance and I don't want to miss a simple trick that PHP can already handle.
Here's my attempt at a very manual loop using process, the idea is to simply sort the numbers into containers for array_unshifting. I'm sure this is terrible and I'd love someone to do this in five lines or less :)
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
// Build the container array
$numbers = array_fill_keys(array_unique($array),array());
// Assignment
foreach( $array as $number )
{
$numbers[ $number ][] = $number;
}
// Worker Loop
$output = array();
while( empty( $numbers ) === false )
{
foreach( $numbers as $outer => $inner )
{
$output[] = array_shift( $numbers[ $outer ] );
if( empty( $numbers[ $outer ] ) )
{
unset( $numbers[ $outer ] );
}
}
}
var_dump( $output );
I think I'd look at this not as a sorting problem, but alternating values from multiple lists, so rather than coming up with sets of distinct numbers I'd make sets of the same number.
Since there's no difference between one 1 and another, all you actually need is to count the number of times each appears. It turns out PHP can do this for you with aaray_count_values.
$sets = array_count_values ($input);
Then we can make sure the sets are in order by sorting by key:
ksort($sets);
Now, we iterate round our sets, counting down how many times we've output each number. Once we've "drained" a set, we remove it from the list, and once we have no sets left, we're all done:
$output = [];
while ( count($sets) > 0 ) {
foreach ( $sets as $number => $count ) {
$output[] = $number;
if ( --$sets[$number] == 0 ) {
unset($sets[$number]);
}
}
}
This algorithm could be adapted for cases where the values are actually distinct but can be put into sets, by having the value of each set be a list rather than a count. Instead of -- you'd use array_shift, and then check if the length of the set was zero.
You can use only linear logic to sort using php functions. Here is optimized way to fill data structures. It can be used for streams, generators or anything else you can iterate and compare.
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
$chunks = [];
$index = [];
foreach($array as $i){
if(!isset($index[$i])){
$index[$i]=0;
}
if(!isset($chunks[$index[$i]])){
$chunks[$index[$i]]=[$i];
} else {
$chunks[$index[$i]][] = $i;
}
$index[$i]++;
}
$result = call_user_func_array('array_merge', $chunks);
print_r($result);
<?php
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
while($array) {
$n = 0;
foreach($array as $k => $v) {
if($v>$n) {
$result[] = $n = $v;
unset($array[$k]);
}
}
}
echo implode(',', $result);
Output:
1,2,3,4,5,1,2,3,4,5,1,2,3,4,1,2,4
New, more elegant, more performant, more concise answer:
Create a sorting array where each number gets its own independent counter to increment. Then use array_multisort() to sort by this grouping array, then sort by values ascending.
Code: (Demo)
$encounters = [];
foreach ($array as $v) {
$encounters[] = $e[$v] = ($e[$v] ?? 0) + 1;
}
array_multisort($encounters, $array);
var_export($array);
Or with a functional style with no global variable declarations: (Demo)
array_multisort(
array_map(
function($v) {
static $e;
return $e[$v] = ($e[$v] ?? 0) + 1;
},
$array
),
$array
);
var_export($array);
Old answer:
My advice is functionally identical to #El''s snippet, but is implemented in a more concise/modern/attractive fashion.
After ensuring that the input array is sorted, make only one pass over the array and push each re-encountered value into its next row of values. The $counter variable indicates which row (in $grouped) the current value should be pushed into. When finished looping and grouping, $grouped will have unique values in each row. The final step is to merge/flatten the rows (preserving their order).
Code: (Demo)
$grouped = [];
$counter = [];
sort($array);
foreach ($array as $v) {
$counter[$v] = ($counter[$v] ?? -1) + 1;
$grouped[$counter[$v]][] = $v;
}
var_export(array_merge(...$grouped));
Is there any way to get the key range of same values and make a new array?
Let's say we have an Array Like this in php :
$first_array = ['1'=>'a','2'=>'a','3'=>'a','4'=>'b','5'=>'b','6'=>'a','7'=>'a'];
How can i get this array? Is there any function for this?
$second_array = ['1-3'=>'a','4-5'=>'b','6-7'=>'a'];
Loop through it, extract the keys, generate the ranges and insert to the new array -
$first_array = ['1'=>'a','2'=>'a','3'=>'a','4'=>'b','5'=>'b'];
$flip = array();
foreach($first_array as $key => $val) {
$flip[$val][] = $key;
}
$second_array = [];
foreach($flip as $key => $value) {
$newKey = array_shift($value).' - '.end($value);
$second_array[$newKey] = $key;
}
Output
array(2) {
["1 - 3"]=>
string(1) "a"
["4 - 5"]=>
string(1) "b"
}
regarding your first question you can get range of each value using foreach() loop.
$first_array = ['1'=>'a','2'=>'a','3'=>'a','4'=>'b','5'=>'b'];
foreach($first_array as $key=>$value)
{
//do your coding here, $key is the index of the array and $value is the value at that range, you can use that index and value to perform array manipulations
}
Regarding your second question it not exactly clear what are trying to implement there. But what ever you want to do like creating a new array with modified index and other things can be done within this foreach() loop itself
I hope this helps you.
If someone is still looking for an answer, here is what I did.
Given the array
$first_array = ['0'=>'a',
'1'=>'a',
'2'=>'a',
'3'=>'a',
'4'=>'a',
'5'=>'b',
'6'=>'b',
'7'=>'a',
'8'=>'a']
I build a multidimensional array, in which each element is an array of three more elements:
[0] - The value in the first array
[1] - The key where the value starts repeating
[2] - The last key where the value stops repeating
The code
$arrayRange = [];
for($i = 0; $i < count($first_array); $i++){
if(count($arrayRange) == 0){
// The multidimensional array is still empty
$arrayRange[0] = array($first_array[$i], $i, $i);
}else{
if($first_array[$i] == $arrayRange[count($arrayRange)-1][0]){
// It's still the same value, I update the value of the last key
$arrayRange[count($arrayRange)-1][2] = $i;
}else{
// It's a new value, I insert a new array
$arrayRange[count($arrayRange)] = array($first_array[$i], $i, $i);
}
}
}
This way you get a multidimensional array like this:
$arrayRange[0] = array['a', 0, 4];
$arrayRange[1] = array['b', 5, 6];
$arrayRange[2] = array['a', 7, 8];
I have this array in php,
$mainArray = array(
array("apple","two", "grren"),
array("samsung","five", "red"),
array("microsoft","one","gray"),
array("apple","nine", "blue"),
array("samsung","ten", "white"),
array("nokia","seven", "yellow")
);
I can easily loop through it and extract all the first entries of each array like this:
foreach($mainArray as $w => $n) {
$whatever = $mainArray[$w][0];
}
I'm trying to count how many entries are the same in the first element of each array, and have a result of something like:
apple (2)
samsung (2)
microsoft (1)
nokia (1)
I'm just not sure what is the correct way to do this.
Thank you in advance.
print_r(
array_count_values(
array_map('array_shift', $mainArray)
)
);
Output (Demo):
Array
(
[apple] => 2
[samsung] => 2
[microsoft] => 1
[nokia] => 1
)
So even I am a big fan of foreach, why did I not use it here?
First of all, to count values in an array, in PHP we have array_count_values. It does the job for us.
So the only problem left was to get all the first items into an array to count them with array_count_values. That is a typical mapping job, and I like mapping, too, next to foreach so I tried if array_map together with array_shift worked - and it did.
However you might want to look for a function called array_column. It's not yet available with PHP itself, but as PHP code in another answer:
$counts = array_count_values(array_column($mainArray, 0));
$count = array();
foreach($mainArray as $array) {
$first = $array[0];
if(!isset($count[$first])) $count[$first] = 0;
$count[$first]++;
}
print_r($count);
Collect every first element of the deep arrays by pushing them into a new array ($result in my example) and then call array_count_values() on that array. Like so:
$mainArray = array(
array("apple","two", "grren"),
array("samsung","five", "red"),
array("microsoft","one","gray"),
array("apple","nine", "blue"),
array("samsung","ten", "white"),
array("nokia","seven", "yellow")
);
$result = array();
foreach( $mainArray as $k => $v )
{
// let's continue if $v is not an array or is empty
if( !is_array( $v ) || empty( $v ) ) continue;
$result[] = $v[ 0 ];
}
var_dump( array_count_values( $result ) );
You can loop through the $mainArray to build a full array/list of values and then use array_count_values() on that:
$firstElements = array();
foreach ($mainArray as $arr) {
$firstElements[] = $arr[0];
}
$counts = array_count_values($firstElements);
Another option would be to loop through $mainArray and insert the value as an index for an array (if it doesn't already exist) and then increment it each time (this will do the same thing as array_count_values() in the end):
$counts = array();
foreach ($mainArray as $arr) {
if (!isset($counts[$arr[0]])) $counts[$arr[0]] = 0;
$counts[$arr[0]]++;
}
You can do it just like this:
foreach($mainArray as $n) {
$count[$n[0]] = isset($count[$n[0]]) ? $count[$n[0]]++ : 1;
}
var_dump($count); //should give you something like
/*
array(4) {
["apple"]=>
int(2)
["samsung"]=>
int(2)
["microsoft"]=>
int(1)
["nokia"]=>
int(1)
}
*/
So here's the input:
$in['a--b--c--d'] = 'value';
And the desired output:
$out['a']['b']['c']['d'] = 'value';
Any ideas? I've tried the following code without any luck...
$in['a--b--c--d'] = 'value';
// $str = "a']['b']['c']['d";
$str = implode("']['", explode('--', key($in)));
${"out['$str']"} = 'value';
This seems like a prime candidate for recursion.
The basic approach goes something like:
create an array of keys
create an array for each key
when there are no more keys, return the value (instead of an array)
The recursion below does precisely this, during each call a new array is created, the first key in the list is assigned as the key for a new value. During the next step, if there are keys left, the procedure repeats, but when no keys are left, we simply return the value.
$keys = explode('--', key($in));
function arr_to_keys($keys, $val){
if(count($keys) == 0){
return $val;
}
return array($keys[0] => arr_to_keys(array_slice($keys,1), $val));
}
$out = arr_to_keys($keys, $in[key($in)]);
For your example the code above would evaluate as something equivalent to this (but will work for the general case of any number of -- separated items):
$out = array($keys[0] => array($keys[1] => array($keys[2] => array($keys[3] => 'value'))));
Or in more definitive terms it constructs the following:
$out = array('a' => array('b' => array('c' => array('d' => 'value'))));
Which allows you to access each sub-array through the indexes you wanted.
$temp = &$out = array();
$keys = explode('--', 'a--b--c--d');
foreach ($keys as $key) {
$temp[$key] = array();
$temp = &$temp[$key];
}
$temp = 'value';
echo $out['a']['b']['c']['d']; // this will print 'value'
In the code above I create an array for each key and use $temp to reference the last created array. When I run out of keys, I replace the last array with the actual value.
Note that $temp is a REFERENCE to the last created, most nested array.
Is it possible to edit the key once it has been made?
I know you can create an array with a different key but I couldn't see anything on the php site about editing the key latter.
Original array:
Array
(
[0] => first
[1] => color
)
What I would like:
Array
(
[newName] => first
[1] => color
)
If you want to change the key of an item, you must either set the value with the new key and unset() the old one (this technique changes the order of the array):
$arr['newName'] = $arr[0];
unset($arr[0]);
or use a wrapper that forgoes looping and allows you to modify the keys, as such:
function array_change_key(&$array, $search, $replace) {
$keys = array_keys($array);
$values = array_values($array);
// Return FALSE if replace key already exists
if(array_search($replace, $keys) !== FALSE) return FALSE;
// Return FALSE if search key doesn't exists
$searchKey = array_search($search, $keys);
if($searchKey === FALSE) return FALSE;
$keys[$searchKey] = $replace;
$array = array_combine($keys, $values);
return TRUE; // Swap complete
}
Here's an alternate, simple approach, which is probably fairly efficient, as long as you do all of your re-keying for each array in a single call:
<?php
function mapKeys(array $arr, array $map) {
//we'll build our new, rekeyed array here:
$newArray = array();
//iterate through the source array
foreach($arr as $oldKey => $value) {
//if the old key has been mapped to a new key, use the new one.
//Otherwise keep the old key
$newKey = isset($map[$key]) ? $map[$key] : $oldKey;
//add the value to the new array with the "new" key
$newArray[$newKey] = $value;
}
return $newArray;
}
$arr = array('first', 'color');
$map = array(0 => 'newName');
print_r(mapKeys($arr, $map));
$array = array('foo', 'bar');
$array['newName'] = $array[0];
unset($array[0]);
That's pretty much the only thing you can do.