I have two arrays, array1 and array2 that I am using to populate a table, so that array1[5] and array2[5] both fill the same row but I want to write a function that removes both array1[i] and array2[i] if array1[i] is a duplicate of array1[j] for some j less than i, where i is an arbitrary positive integer.
To accomplish this I was to work out the indices of duplicate values in array1 and then use this information to delete the entries from both array1 and array2 for these indices, before populating my table.
Any ideas gratefully received.
Thanks.
The array_unique function removes dupes, but preserves keys. Then you can just iterate through the other array and remove the keys that don't exist in the first one.
$array1 = array_unique($array1);
foreach ($array2 as $key => $val) {
if (!array_key_exists($key,$array1)) unset($array2[$key]);
}
$array1_keys = array_keys($array1); // all keys
$unique = array_keys(array_unique($array1)); // keys of unique values
$duplicate = array_diff($array1_keys, $unique_keys); // keys of the duplicate values
foreach($duplicate as $key){
unset($array2[$key]);
}
Sidenote: Be aware that array_diff uses string casting for comparison. If your array contains non scalar values you should have a look at array_udiff.
Edit: Mingos post in my eyes did not fully suit the question, looks like i was wrong :D
If your array keys are meaningful and not just a 0-based index and you want to keep the duplicate entry with the lowest key (as you indicate might be the case from your question) then you need to sort the array first. If you don't, you will get the first entry in the array for each duplicate value and not entry with the lowest key. Compare
$array = array( 5 => 'foo', 1 => 'bar', 2 => 'foo', 3 => 'bar' );
$array = array_unique( $array );
var_dump( $array );
with
$array = array( 5 => 'foo', 1 => 'bar', 2 => 'foo', 3 => 'bar' );
ksort( $array );
$array = array_unique( $array );
var_dump( $array );
In asociative arrays, you can get easily a counter of item to detect repeated and the first ocurrence with something as simple as (in this case using an associative array, where $array_key is the key to count) using array_count_values:
$index = null;
$array_by_columns = array_count_values(array_column($associative_array, $array_key);
foreach($array_by_columns) as $key => $ocurrences){
if($ocurrences > 1){
$index = $key;
}
}
If you have $index there are repeated element and also is the index of in the array.
A more compacted way in a line:
$index = null;
foreach(array_count_values(array_column($associative_array, $array_key)) as $key => $ocurrences)if($ocurrences > 1) $index = $key;
Data example:
[ARRAY] (start)
n0
KEY = 0
VALUE = {"user":"1","points":"10"}
n1
KEY = 1
VALUE = {"user":"4","points":"10"}
n2
KEY = 2
VALUE = {"user":"5","points":"30"}
n3
KEY = 3
VALUE = {"user":"1","points":"40"}
[ARRAY] (end)
More info:
array_count_values
array_column
Related
I've got the following array
$array = [
1 => 'test',
2 => 'test',
3 => 'test',
4 => 'another'
];
What i want to obtain from this is:
The count of value 'test' $values (It's variable);
After that i will need to get all keys for value 'test' do to the next assumption:
if (\count($values) > 1) {
// do something with keys of value 'test'
echo $keysOfThatValue;
}
I will need to do the upper assumption to all of the array elements grouped by array value.
You can use array_keys with a search argument, then just count those:
$count = count($keys = array_keys($array, 'test'));
Or following your code. The if only executes if test is found and keys are returned:
if($keys = array_keys($array, 'test')) {
// do something with $keys of value 'test'
}
array_filter() can be used to get an array of only entries with the value "test".
Suppose I have an array of indexes, A.
Suppose I have an array B, where every key is an array containing some indexes or number.
I would know which of the entries in B contain some index appearing in A.
For example (in php style):
A = [3,45,67,8]
B = [ 1 => [1,6,81],
2 => [5,67,3,4,5,66,6],
3 => [55,56,57,58],
4 => [45,80,81,82]
]
The entries of B which contain some value of A are 2 and 4.
So the function I would make should be:
function solution = filter(A,B) // solution = [2,4]
Now, with a brute loop, looping over entries of B, the complexity will be
O(nm), where n is #B and m is the size of longest row in B.
There are smarter solutions?
Edit #2:
By moving all values to be compared to key positions, php can more than double efficienct (by my demo's system time metric).
Furthermore, if you have duplicate values in your subsets, calling array_flip() will reduce the size by disallowing duplicate keys.
Code: (Demo)
$A = array_flip($A); // prepare for key comparisons
$result = [];
foreach ($B as $key => $haystack) {
if (array_intersect_key(array_flip($haystack), $A)) {
$result[] = $key;
}
}
var_export($result);
Edit:
Whenever you want to optimize array searching with php, it is often best to try to prepare your data in a way which allows php to leverage its strength with hash tables. https://codedmemes.com/lib/best-performance-array-intersection/
Consider this preparation...
Code: (Demo)
foreach ($B as $key => $haystack) {
foreach ($haystack as $hay) {
$C[$hay][] = $key;
}
}
var_export(array_keys(array_flip((array_merge(...array_intersect_key($C, array_flip($A)))))));
Output:
array (
0 => 1,
1 => 2,
2 => 4,
)
The nested foreach() loops generate a collection of subarrays which have unique values from B's subarrays as keys and $B's original keys as new subarray values.
array_intersect_key() checks the keys which php does much faster than checking values. (see first hyperlink article)
Then array_merge(...) flattens the subarrays into a single 1-dim array.
Finally array_flip() and array_keys() removes duplicates and re-indexes the results.
I don't know exactly how array_intersect() works its magic in terms of efficiency, but this is probably how I'd go about it:
Code: (Demo)
$A = [3,45,67,8];
$B = [ 1 => [1,6,8],
2 => [5,67,3,4,5,66,6],
3 => [55,56,57,58],
4 => [45,80,81,82]
];
$result = [];
foreach ($B as $key => $haystack) {
if (array_intersect($haystack, $A)) { // generate an array with co-existing values
$result[] = $key; // add key if array_intersect makes a non-empty array
}
}
var_export($result);
Output:
array (
0 => 1,
1 => 2,
2 => 4,
)
I suppose if there is a "downside" to using array_intersect() it would be that it will bother to make multiple matches, when only a single match per row is necessary. For this reason, array_search() or a breaking loop might have advantages.
Code: (Demo)
$result = [];
foreach ($B as $key => $haystack) {
foreach ($haystack as $hay) {
if (in_array($hay, $A)) {
$result[] = $key;
break;
}
}
}
var_export($result); // same result
I have an array:
$ids = array(1 => '3010', 2 => '10485', 3 => '5291');
I want to create a new array that takes the values of the $ids array and sets them as the keys of a new array, having the same value.
The final array would be:
$final = array('3010' => 'Green', '10485' => 'Green', '5291' => 'Green');
This will be used in apc_add().
I know I can accomplish this by looping thru it.
$final = array();
foreach($ids as $key => $value):
$final[$value] = 'Green';
endforeach;
But I was wondering if there was php function that does this without having to use a forloop, thanks!
You are looking for array_fill_keys.
$final = array_fill_keys($ids, "Green");
However, be aware that strings that are decimal representations of integers are actually converted to integers when used as array keys. This means that in your example the numbers that end up as keys in $final will have been transformed to integers. Most likely won't make a difference in practice, but you should know about it.
You can do with array_fill_keys this way:
$final = array_fill_keys($ids, "Green");
I have a PHP array that has literal_key => value. I need to shift the key and value off the beginning of the array and stick it at the end (keeping the key also).
I've tried:
$f = array_shift($fields);
array_push($fields, $f);
but this loses the key value.
Ex:
$fields = array ("hey" => "there", "how are" => "you");
// run above
this yields:
$fields = array ("how are" => "you", "0" => "there");
(I need to keep "hey" and not have 0) any ideas?
As far as I can tell, you can't add an associative value to an array with array_push(), nor get the key with array_shift(). (same goes for pop/push). A quick hack could be:
$fields = array( "key0" => "value0", "key1" => "value1");
//Get the first key
reset($fields);
$first_key = key($fields);
$first_value = $fields[$first_key];
unset($fields[$first_key]);
$fields[$first_key] = $first_value;
See it work here. Some source code taken from https://stackoverflow.com/a/1028677/1216976
You could just take the 0th key $key using array_keys, then set $value using array_shift, then set $fields[$key] = $value.
Or you could do something fancy like
array_merge( array_slice($fields, 1, NULL, true),
array_slice($fields, 0, 1, true) );
which is untested but has the right idea.
This is sort of two questions, but there may be one overall answer for the whole problem. I have an array that I need to append onto another array. Both arrays must have specific numeric keys. My problems are:
I need the numeric keys for the array that I am appending onto to be preserved.
array_splice() and array_merge() won't work to join the arrays because numeric keys in both arrays will be reset.
I need to make the keys of the newly added elements to be n through n + x, meaning if n is 100 and x is 25, the keys for the newly added elements should be 100 through 125.
Can anyone think of a somewhat efficient way of doing this?
EDIT
For anyone curious, found a better way of adding the correct keys to the array.
// add correct keys
$array_segment = array_combine(range($offset, $offset + count($array_segment) - 1), $array_segment);
// merge arrays while maintaining keys
$first_array = $first_array + $array_segment;
I think this is a very simple solution but, if I understand well what you want, it works and it's fast. In my opinion you can use this approach:
$array1 = array(1 => 'a', 2 => 'b', 3 => 'c');
$array2 = array(4 => 'd', 5 => 'e', 6 => 'f');
foreach($array2 as $key => $value)
$array1[$key] = $value;
var_dump($array1);
$array1[] = 'g';
$array1[] = 'h';
var_dump($array1);
You can see the result here:
http://codepad.org/ogD9drpK
Another way which will avoid the foreach is to execute this instruction:
// avoid the loop
//foreach($array2 as $key => $value)
// $array1[$key] = $value;
$array1 += $array2;
You can see the result here:
http://codepad.org/cZxCfRn6