Loop an array which has gapped numeric keys - php

After using array_unique, an array without the duplicate values is removed. However, it appears that the keys are also removed, which leaves gaps in an array with numerical indexes (although is fine for an associative array). If I iterate using a for loop, I have to account for the missing indexes and just copy the keys to a new array, but that seems clumsy.

$foo = array_values($foo); will re-number an array for you

Instead of using for loops it sounds like you should use foreach loops. Apparently you don't care about indexes anyway since you are renumbering them.
This loop:
for ($i = 0; $i < $loopSize; $i++)
{
process($myArray[$i]);
}
turns into
foreach($myArray as $key=> $value)
{
process($value);
/** or process($myArray[$key]); */
}
or even more simply
foreach($myArray as $value)
{
process($value);
}

In the few cases I've tried using for instead of foreach, I soon regretted it.
It can really always be avoided, you can even use foreach but ignore the values and use the key, almost forgetting that its a foreach instead of for, but avoiding any gaps in your keys and automatically have your bounds taken care of without length/min/max functions or anything.
ex.
foreach($myArray as $key=>$val)
{
myArray[$key] = myFunction(myArray[$key]);
}
I've particularly found this useful with parallel arrays.
$a = getA(); $b = getB();
foreach($a as $key=>val)
{
$sql = "INSERT INTO table (field1, field2) VALUES ($a[$key], $b[$key])";
}

Related

Return duplicate values keys from array

I need to determine the keys of values, that have duplicates from an array.
What I came up with is:
$duplicates_keys = array();
$unique = array_unique($in);
$duplicates = array_diff_assoc($in, $unique);
foreach ($in as $key => $val){
if (in_array($val,$duplicates)){
$duplicates_keys[]=$key;
}
}
Which works, but that's pretty resource intensive, is there a faster way to do this?
As per my comment, i doubt this is a bottleneck. However you can reduce your iterations to once through the array, as follows:
$temp=[];
$dup=[];
foreach ($in as $key=>$val) {
if(isset($temp[$val])){
$dup[]=$key;
}else{
$temp[$val]=0;
}
}
Note that the value is set as an array key in temp, so you can use O(1) isset rather than in_array, which must search the full array until the value is found.
This is theoretically faster than your example, but you would need to profile it to be sure (as you should have already done to ascertain your current code is slow).
Probably you can do something else that has a far greater impact, like caching or a better database query
Use array_intersect() for this.
$duplicates_keys = array_intersect($in, $duplicates);
array_intersect()

Create an Array with only unique values

I looping through a large dataset (contained in a multidimensional associative array $values in this example) with many duplicate index values with the goal of producing an array containing only the unique values from a given index 'data'.
Currently I am doing this like:
foreach ($values as $value) {
$unique[$value['data']] = true;
}
Which accomplishes the objective because duplicate array keys simply get replaced. But this feels a bit odd since the indexes themselves don't actually contain any data.
It was suggested that I build the array first and then use array_unique() to removes duplicates. I'm inclined to stick with the former method but am wondering are there pitfalls or problems I should be aware of with this approach? Or any benefits to using array_unique() instead?
I would do it like this.
$unique = array();
foreach($values as $value) {
if(!in_array($value, $unique) {
$unique[] = value;
}
}

Problems with SORT_FLAG_CASE flag

I am tring to do (like) a 2 dimensional array, case insensitive.
I have:
foreach ($rows as $key=>$row) {
$names[$key]=$row['Name'];
}
array_multisort($rows,SORT_STRING|SORT_FLAG_CASE,$names);
The above ends up producing the same result (with or without case flag).
Sick of staring at this, any ideas from somebody outside?
First of all SORT_FLAG_CASE is only available in PHP v5.4+ so I suggest checking which version of PHP you are running (maybe 'uksort' could help if 5.3ish).
If not, make sure all the values that you put into $names lowercase or uppercase.
You have the order of the arguements $rows and $names reversed in the call to array_multisort.
Lastly if it comes from a database (or some other manner that means you cant change the data on the way into the array) then you can use array_walk.
Hope that helps
Since I ran into this with PHP 5.3.16, I thought I'd share my simple solution: just convert your keys to lower (or upper) case, e.g.:
foreach ($rows as $key=>$row) {
$names[$key]=strtolower($row['Name']);
}
array_multisort($names,SORT_STRING,$rows);
I also swapped the $rows & $names and removed the SORT_FLAG_CASE (to get rid of the log message).
You can also do a sort within a sort, so you can use usort with strcasecmp:
foreach ($rows as $key=>$row) {
$names[$key]=row['Name'];
}
array_multisort(usort($names,strcasecmp),$rows);
Above answer didn't work, because the first array got mingled while the other one didn't. So I wrote a general multidimensional array case insensitive compare function. It also can use multiple keys:
function array_casecmp($keys) {
if (gettype($keys) != "array") $keys = func_get_args();
return function ($a, $b) use ($keys) {
foreach($keys as $value) {
$akeys = $akeys . $a[$value];
$bkeys = $bkeys . $b[$value];
}
return strcasecmp($akeys, $bkeys);
};
}
Use it like:
usort($files,strcasecmp(array(0,1)); // with standard array
usort($files,strcasecmp(1); // single key
usort($files,strcasecmp(0,1); // arguments are converted to array
usort($files,strcasecmp("dir","link")); // you can also use symbolic keys
usort($files,strcasecmp(0,1,2,3,4,...); // use as many keys as you like
Keep in mind that the keys are concatenated, so maybe you have to use str_pad in your array colums to keep them seperated in the right way.

PHP push values into associative array

I can't find an answer to this anywhere.
foreach ($multiarr as $array) {
foreach ($array as $key=>$val) {
$newarray[$key] = $val;
}
}
say $key has duplicate names, so when I am trying to push into $newarray it actually looks like this:
$newarray['Fruit'] = 'Apples';
$newarray['Fruit'] = 'Bananas';
$newarray['Fruit'] = 'Oranges';
The problem is, the above example just replaces the old value, instead of pushing into it.
Is it possible to push values like this?
Yes, notice the new pair of square brackets:
foreach ($multiarr as $array) {
foreach ($array as $key=>$val) {
$newarray[$key][] = $val;
}
}
You may also use array_push(), introducing a bit of overhead, but I'd stick with the shorthand most of the time.
I'll offer an alternative to moonwave99's answer and explain how it is subtly different.
The following technique unpacks the indexed array of associative arrays and serves each subarray as a separate parameter to array_merge_recursive() which performs the merging "magic".
Code: (Demo)
$multiarr = [
['Fruit' => 'Apples'],
['Fruit' => 'Bananas'],
['Fruit' => 'Oranges'],
['Veg' => 'Carrot'],
//['Veg' => 'Leek'],
];
var_export(
array_merge_recursive(...$multiarr)
);
As you recursively merge, if there is only one value for a respective key, then a subarray is not used, if there are multiple values for a key, then a subarray is used.
See this action by uncommenting the Leek element.
p.s. If you know that you are only targetting a single column of data and you know the key that you are targetting, then array_column() would be a wise choice.
Code: (Demo)
var_export(
['Fruit' => array_column($multiarr, 'Fruit')]
);

PHP key value pairs vs. arrays

I'm trying to pass key values pairs within PHP:
// "initialize"
private $variables;
// append
$this->variables[] = array ( $key = $value)
// parse
foreach ( $variables as $key => $value ) {
//..
}
But it seems that new arrays are added instead of appending the key/value, nor does the iteration work as expect. Please let me know what the proper way is.
Solution
$this->variables[$key] = $value;
did the trick - the iteration worked as described above.
I think you may be looking for:
$this->variables[$key] = $value;
The way you have it right now you are creating an array of arrays, so you would have to do this:
foreach($this->variables as $tuple) {
list($key, $value) = $tuple;
}
Referring to Perl, but helps understand the difference between hashes and arrays:
Some people think that hashes are like arrays (the old name 'associative array' also indicates this, and in some other languages, such as PHP, there is no difference between arrays and hashes.), but there are two major differences between arrays and hashes. Arrays are ordered, and you access an element of an array using its numerical index. Hashes are un-ordered and you access a value using a key which is a string.
Source: http://perlmaven.com/perl-hashes

Categories