How to remove values from an array whilst renumbering numeric keys - php

I have an array which may contain numeric or associative keys, or both:
$x = array('a', 'b', 'c', 'foo' => 'bar', 'd', 'e');
print_r($x);
/*(
[0] => a
[1] => b
[2] => c
[foo] => bar
[3] => d
[4] => e
)*/
I want to be able to remove an item from the array, renumbering the non-associative keys to keep them sequential:
$x = remove($x, "c");
print_r($x);
/* desired output:
(
[0] => a
[1] => b
[foo] => bar
[2] => d
[3] => e
)*/
Finding the right element to remove is no issue, it's the keys that are the problem. unset doesn't renumber the keys, and array_splice works on an offset, rather than a key (ie: take $x from the first example, array_splice($x, 3, 1) would remove the "bar" element rather than the "d" element).

This should re-index the array while preserving string keys:
$x = array_merge($x);

You can fixet with next ELEGANT solution:
For example:
<?php
$array = array (
1 => 'A',
2 => 'B',
3 => 'C'
);
unset($array[2]);
/* $array is now:
Array (
1 => 'A',
3 => 'C'
);
As you can see, the index '2' is missing from the array.
*/
// SOLUTION:
$array = array_values($array);
/* $array is now:
Array (
0 => 'A',
1 => 'C'
);
As you can see, the index begins from zero.
*/
?>

I've come up with this - though I'm not sure if it's the best:
// given: $arr is the array
// $item is the item to remove
$key = array_search($item, $arr); // the key we need to remove
$arrKeys = array_keys($arr);
$keyPos = array_search($key, $arrKeys); // the offset of the item in the array
unset($arr[$key]);
array_splice($arrKeys, $keyPos, 1);
for ($i = $keyPos; $i < count($arrKeys); ++$i) {
if (is_int($arrKeys[$i])) --$arrKeys[$i]; // shift numeric keys back one
}
$arr = array_combine($arrKeys, $arr); // recombine the keys and values.
There's a few things I've left out, just for the sake of brevity. For example, you'd check if the array is associative, and also if the key you're removing is a string or not before using the above code.

Try array_diff() it may not order the new array correctly though
if not the following should work
You will need to iterate over it in the remove function.
function remove($x,$r){
$c = 0;
$a = array();
foreach ($x as $k=>$v){
if ($v != $r) {
if (is_int($k)) {
$a[$c] = $v;
$c++;
}
else {
$a[$k] = $v;
}
}
}
return $a;
}
DC

I don't think there is an elegant solution to this problem, you probably need to loop to the array and reorder the keys by yourself.

Related

PHP: preserve order of array_values

If I have an associative array that is structured like
(
1 => 'a',
2 => 'b',
0 => 'c'
)
where all of the keys are numeric, will array_values ALWAYS guarantee that the values occur chronologically, in the new array, based on their previous keys' values, i.e. ['c', 'a', 'b']?
If not, how can I accomplish this instead?
No, array_values() will not reorder the values in any way. It doesn't care about keys.
Its effective implementation is basically this:
function array_values_impl(array $array)
{
$newArray = [];
foreach ($array as $item) {
$newArray[] = $item;
}
return $newArray;
}
If you want to sort the array using the keys, use ksort().
You can accomplish by first sorting the array with keys and getting values by array_values function.
For example
$array = array(
1 => 'a',
2 => 'b',
0 => 'c'
);
ksort($array);
print_r(array_values($array));
Output:
Array
(
[0] => c
[1] => a
[2] => b
)

Edit array placement

I am having a simple issue with ordering an array elements.
Let's assume we have an array like this
[0]=>zero
[1]=>one
[2]=>two
[3]=>three
What i want is a way to move some elements to first position for example move one and two to first positions so i will have:
[1]=>one
[2]=>two
[0]=>zero
[3]=>three
and this should be done without knowing the current position of the element in the array which means it should be done by specifying the name of wanted element to move.
I thought about array_splice() but it won't work since i should specify the key of the element in array.
You could build two arrays (one with the searched values and one without) and merge them. Keep in mind that if you want to keep numeric keys, you should use the + operator instead of array_merge :
<?php
$array = array('zero', 'one', 'two', 'three');
$searched_values = array('one', 'two');
$array_first = $array_second = array();
foreach ($array as $key=>$value) {
if (in_array($value, $searched_values))
$array_first[$key] = $value;
else
$array_second[$key] = $value;
}
$array_end = $array_first + $array_second;
echo '<pre>'; print_r($array_end); echo '</pre>';
Returns :
Array
(
[1] => one
[2] => two
[0] => zero
[3] => three
)
EDIT : Keeping the $searched_values order
In this case, you just need to loop over the serached values in order to build an array of found values and simultaneously delete the initial array's entry :
<?php
$array = array('zero', 'one', 'two', 'three');
$searched_values = array('one', 'three', 'two');
$array_found = array();
foreach ($searched_values as $key=>$value) {
$found_key = array_search($value, $array);
if ($found_key !== false) {
$array_found[$found_key] = $value;
unset($array[$found_key]);
}
}
$array_end = $array_found + $array;
echo '<pre>'; print_r($array_end); echo '</pre>';
?>
Returns :
Array
(
[1] => one
[3] => three
[2] => two
[0] => zero
)
The only way I can think to do this is to remove it then add it:
$key = array_search('one', $array); // $key = 1;
$v = $my_array[$key];
unset($my_array[$key]);
array_unshift($my_array, $v);
Output:
[0] => one
[1] => zero
[2] => two
[3] => three
UPDATE:
If you use array_unshift it will not override your current element.

Randomly select words from multi-dimension array

I have a multi-dimensional array and from where i want to choose 11 different words. Each word from different array index.
Here is the array link: My multi-dimensional array
array (
'w' =>
array (
0 => 'walls',
1 => 'well',
2 => 'why',
),
'e' =>
array (
0 => 'end',
),
'a' =>
array (
0 => 'advantage',
1 => 'afford',
2 => 'affronting',
3 => 'again',
4 => 'agreeable',
5 => 'ask',
6 => 'at',
),
'c' =>
array (
0 => 'children',
1 => 'civil',
2 => 'continual',
)
);
My Desire Output:
From w => well
From e => end
From a => again
and so on.
Output like: array(well, end, again, ...) as array.
Use the following code:
$f = array_keys($result); // grouping the indices, namely, the characters
$a = "";
for($c=0;$c<count($f);$c++){
$a .= $f[$c];
} // grouping the indices stored in array $f to a string, $a
$words = array();
for($c=0;$c<11;$c++){
$random = $a[rand(0,strlen($a)-1)];
$k = $result[$random];
// $k stores the array of the character index, stored in $result
$random2 = rand(0,count($k)-1);
$words[$c] = $k[$random2];
// choose a word from a given character array
$a = preg_replace("/".$random."/","",$a);
// remove the character from $a to prevent picking words which start with the same character
}
print_r($words);
I've tested and it was proved working
https://3v4l.org/qi1VP
You can achieve this usin array_rand() function :
PHP
$words = [];
$limit = 3; //Replace this with your limit, 11
$count = 0;
shuffle($array);
foreach($array as $key => $value) {
$words[] = $value[array_rand($value)];
$count++;
if ($limit == $count) {
break;
}
}
EvalIn
Check Online, and let me know.
using shuffle and array_slice you can get what you want.
A shuffle function makes your array random, and array slice slice 11 sub array from it.
Array slice takes 3 argument, first one is the array, second one is the offset from where you want to start and last one how much you need to cut.
$words = array();
shuffle($result);
$res = array_slice($result, 0, 11);
foreach($res as $key => $value){
shuffle($value);
$words[] = $value[0];
}
print_r($words);

Combine Indexed arrays with the same keys

I am using the Flot jQuery plugin to create a graph on how many visitors there have been per platform. I would like to create a 4th line with total visitors, calculated by previously retrieved data.
I need to combine several multi-dimensional Indexed arrays, but not simply merging them recursively. I.E:
$arr1 = [[2016/05/04,2],[2016/05/03,4],[2016/05/02,6]];
$arr2 = [[2016/05/04,1],[2016/05/03,3],[2016/05/02,2]];
$arr3 = [[2016/05/04,6],[2016/05/03,7],[2016/05/02,8]];
The output should be:
$arrTotal = [[2016/05/04,9],[2016/05/03,14],[2016/05/02,16]];
How do I accomplish this in a (fairly) simple way?
First of all, you cannot declare your dates the way you did:
$arr1 = [[2016/05/04,2],[2016/05/03,4],[2016/05/02,6]];
Because it's going to take 2016, divide it by 5 then divide it by 4. You need to put them into quotes.
$arr1 = [['2016/05/04',2],['2016/05/03',4],['2016/05/02',6]];
But to create an associative array, you should do it this way:
$arr1 = array('2016/05/04' => 2, '2016/05/03' => 4, '2016/05/02' => 6);
$arr2 = array('2016/05/04' => 1, '2016/05/03' => 3, '2016/05/02' => 2);
$arr3 = array('2016/05/04' => 6, '2016/05/03' => 7, '2016/05/02' => 8);
Now all you want to do, is loop through each array and sum them up.
$merge = array();
function mergeArray(Array &$merge, Array $array){
// Loop through each key and value
foreach($array as $key => $value)
// Make sure the value is numeric
if(is_numeric($value)){
if(!isset($merge[$key]))
$merge[$key] = $value;
else
$merge[$key] += $value;
}
}
mergeArray($merge, $arr1);
mergeArray($merge, $arr2);
mergeArray($merge, $arr3);
And now if you dump the $merge:
array(3) {
["2016/05/04"]=>
int(9)
["2016/05/03"]=>
int(14)
["2016/05/02"]=>
int(16)
}
Build a method that will sum the values by respecting the keys of existing values.
$arr1 = array('2016/05/04'=>2,'2016/05/03'=>4,'2016/05/02'=>6);
$arr2 = array('2016/05/04'=>1,'2016/05/03'=>3,'2016/05/02'=>2);
$arr3 = array('2016/05/04'=>2,'2016/05/03'=>7,'2016/05/02'=>8);
function array_sum(&$new_arr,$arr) {
foreach ($arr as $date_key => $num_value) {
// initialize date in new array with 0, if not done previously
if (! isset($new_arr[$date_key])) { $new_arr[$date_key] = 0; }
// add number for indexed element of array
$new_arr[$date_key] += $num_value;
}
}
$new_arr = array();
array_sum($new_array,$arr1);
array_sum($new_array,$arr2);
array_sum($new_array,$arr3);
You are trying to sum up every second value from each nested array relatively to their position in the parent array.There's a short and simple solution using array_map, array_sum and array_column functions:
$groupped = array_map(null, $arr1,$arr2,$arr3);
$result = array_map(function($v){
return [$v[0][0], array_sum(array_column($v, 1))];
}, $groupped);
print_r($result);
The output:
Array
(
[0] => Array
(
[0] => 2016/05/04
[1] => 9
)
[1] => Array
(
[0] => 2016/05/03
[1] => 14
)
[2] => Array
(
[0] => 2016/05/02
[1] => 16
)
)

PHP: take out duplicate digits from an array then print them out

I'm probably [super]overthinking this. I'm trying to analyze an array with values like [1,9], [4,6] [5,5], [6,4], [9,1] and duplicate digits (I'm having a super brain fart and can't even remember the term for numbers like this) remove (the last two) so that only [1,9], [4,6] [5,5] are printed.
I was thinking that turning this array into a string and using preg_match, but I'm pretty sure this wouldn't work even if I had the correct regex.
If you have an array of pairs like this:
$x = array(
array(1,9),
array(4,6),
array(5,5),
array(6,4),
array(9,1)
);
Here is one way to get the unique pairs:
foreach ($x as $pair) {
sort($pair);
$unique_pairs[implode(',', $pair)] = $pair;
}
This uses string representations of each sorted pair as keys in a new array, so the result will have distinct values by definition.
As far as the printing them out part of your question, once you have the unique values you can loop over them and print them out in whichever format you like, for example:
foreach ($unique_pairs as $pair) { vprintf("[%d,%d]<br>", $pair); }
It looks like elements are distributed symmetrically.
We can cut the array in two halves and get only the first half with array_slice():
$array = array(
array(1,9),
array(4,6),
array(5,5),
array(6,4),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 4
[1] => 6
)
[2] => Array(
[0] => 5
[1] => 5
)
)
Demo at Codepad.
ceil() is used to round the number up to the next highest integer if there is an even number of items in the array. Example: if there is 3 items in the array, 5 / 2 will return 2.5, we want 3 items so we use ceil(2.5) which gives 3.
Example with 3 items:
$array = array(
array(1,9),
array(5,5),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 5
[1] => 5
)
)
Example with 4 items:
$array = array(
array(1,9),
array(7,7),
array(7,7),
array(9,1),
);
print_r(array_slice($array, 0, ceil(count($array) / 2)));
Result:
Array(
[0] => Array(
[0] => 1
[1] => 9
)
[1] => Array(
[0] => 7
[1] => 7
)
)
If I'm correct in understanding what you are trying to do, you want to remove the final 2 elements from the array?
There is a function in PHP called array_pop that removes the final element from the array.
$array = array_pop($array);
So if you run this twice, you will remove the final 2 elements from the array.
This is how I'd do it (and I hope I am not overthinking this :))
$stringArray = array();
$stringArray[] = '1,9';
$stringArray[] = '4,6';
$stringArray[] = '5,5';
$stringArray[] = '6,4';
$stringArray[] = '9,1';
foreach($stringArray as &$numString) {
$numString = explode(',', $numString);
usort($numString, function($a, $b) {return $a - $b;});
$numString = implode(',', $numString);
}
$a = array_unique($a);
print_r($a);
You basically explode every element into a subarray, sort it and then implode it back. After calling the array_unique, you're left with unique values in the array.
The output would be
Array
(
[0] => 1,9
[1] => 4,6
[2] => 5,5
)
The result you suggest treats [a,b] as equivalent to [b,a] which makes the problem a lot more complex. The code below gives the result you asked for, but without really understanding what the problem is that you are trying to fix and whether [1,9] is equivalent to [9,1] in the solution:
$a=array(array(1,9),array(4,6),...
$dup=array();
for ($i=0; $i<count($a) -1; $i++) {
for ($j=$i+1; $j<count($a); $j++) {
if (($a[$i][0]==$a[$j[0] && $a[$i][1]==$a[$j[1])
|| ($a[$i][0]==$a[$j[1] && $a[$i][1]==$a[$j[0])) {
$dup[]=$j;
}
}
}
foreach ($dup as $i) {
unset($a[$i]);
}
So I'm actually going to assume your question to have a different meaning than everyone else did. I believe what you're asking is:
How do you filter out array items where a reverse of the item has already been used?
<?php
// The example set you gave
$numberSets = [[1, 9], [4, 6], [5, 5], [6, 4], [9, 1]];
// Initialize an empty array to keep track of what we've seen
$keys = [];
// We use array filter to get rid of items we don't want
// (Notice that we use & on $keys, so that we can update the variable in the global scope)
$numberSets = array_filter($numberSets, function($set) use(&$keys) {
// Reverse the array
$set = array_reverse($set);
// Create a string of the items
$key = implode('', $set);
// Get the reverse of the numbers
$reversedKey = strrev($key);
// If the palindrome of our string was used, return false to filter
if (isset($keys[$reversedKey])) {
return false;
}
// Set the key so it's not used again
// Since $keys is being passed by reference it is updated in global scope
$keys[$key] = true;
// Return true to NOT filter this item, since it or it's reverse were not matched
return true;
});
var_dump($numberSets);

Categories