remove duplicate arrays in loop - php

I have an associative array that might contain duplicates. I am trying to loop through the array and compare the current element with the next element in the array. If there is a duplicate, it should be removed.
The code below removes one instance of the element. In the test array I'm using, I have 3 duplicate part numbers, but my code only removes one. I'm left with two. I only want one to remain.
$length = count($items);
for($i = 0; $i < $length -1; $i++){
if($items[$i]['part_number'] == $items[$i+1]['part_number']){
unset($items[$i+1]);
$items = array_values($items);
}
}
What am I doing wrong here?

You need to loop backwards through the array, and delete the current item.
$length = count($items);
for($i = $length - 1; $i > 0; $i--){
if($items[$i]['part_number'] == $items[$i-1]['part_number']){
unset($items[$i]);
}
}

becuase your code is
The $ items value is in the for statement.
if you want unique array, you have to array_unique function
http://php.net/manual/en/function.array-unique.php

In your case after you unset element, $i++ in for loop, you reindexed your array and you skip one element. Add $i-- if you unset item. Or you can reindex your array after for loop.

This is also a very simple example you can start improving with.
<?php
$test = ['sample', 'sample', 'sample', 'not', 'not', 'no', 'no'];
$test2 = [];
$k = 0;
foreach ($test as $key => $value) {
if ($key == 0) {
$test2[$k] = $value;
$k++;
} else {
if ($test2[$k - 1] != $value) {
$test2[$k] = $value;
$k++;
}
}
}
$test = $test2;
var_dump($test);

One dirty hack is to check again if you have a duplicate by decreasing $i.
for($i = 0; $i < $length -1; $i++){
if($items[$i]['part_number'] == $items[$i+1]['part_number']){
unset($items[$i+1]);
$items = array_values($items);
$i--;
}
}
This way it will again test your previous value against next item in array.
So if 0==1, then next time if 0==2.
Your code did 0==1 then (2)==(3).

Related

Warning: in_array() expects parameter 2 to be array, null given (loop)

I'm looping for string overlaps between two arrays, deleting those values where there is one, so that only the empty values of $check remain, in this case $check[5].
The second one, $check, is multidimensional.
$names = ["bob", "selena", "hailey", "rob", "justin", "robocop"];
$check = [
["justin"], //bob
["justin", "selena", "robocop"], //selena
["justin"], //hailey
["justin", "rob"], //rob
[], //justin
["justin", "selena", "bob"] //robocop
];
for ($i = 0; $i < count($names); $i++) {
for ($j = 0; $j < count($check); $j++) {
if (in_array($names[$i], $check[$j])) {
unset($check[$j]);
}
}
}
The first loop runs through $names, the second through $check.
If the current string from $names ($names[i])
is present in the current array of $check ($check[j])
the array is removed.
However, the console prints a warning: in_array() expects parameter 2 to be array, null given. I find this strange, because $check[j] should be equal to one of the arrays inside of $check.
Is there any way I can fix this?
After a few iterations of the outer loop, you will have unset most of $check. With a for loop like that, you're counting on specific numeric keys being there, but since you unset them, their values are undefined, a.k.a. null.
You can avoid this by using foreach loops instead.
foreach ($names as $name) {
foreach ($check as $key => $array) {
if (in_array($name, $array)) {
unset($check[$key]);
}
}
}
As you are keep on unsetting the $check array, the size of the $check array will go down. Hence the $check won't return a value for the higher values of $j
You can do a minor modification in this code segment as follows
$checkcount = count($check);
for ($i = 0; $i < count($names); $i++) {
for ($j = 0; $j < $checkcount; $j++) {
if (isset($check[$j]) && in_array($names[$i], $check[$j])) {
unset($check[$j]);
}
}

Using an index twice in PHP

I am trying to use a for loop where it looks through an array and tries to make sure the same element is not used twice. For example, if $r or the random variable is assigned the number "3", my final array list will find the value associated with wordList[3] and add it. When the loop runs again, I don't want $r to use 3 again. Example output: 122234, where I would want something along the lines of 132456. Thanks in advance for the help.
for($i = 0; $i < $numWords; $i++){
$r = rand(0, $numWords);
$arrayTrack[$i] == $r;
$wordList[$r] = $finalArray[$i];
for($j = 0; $j <= $i; $j++){
if($arrayTrack[$j] == $r){
# Not sure what to do here. If $r is 9 once, I do not want it to be 9 again.
# I wrote this so that $r will never repeat itself
break;
}
}
Edited for clarity.
Pretty sure you are over complicating things. Try this, using array_rand():
$final_array = array();
$rand_keys = array_rand($wordList, $numWords);
foreach ($rand_keys as $key) {
$final_array[] = $wordList[$key];
}
If $numWords is 9, this will give you 9 random, unique elements from $wordList.
See demo
$range = range(0, $numWords - 1); // may be without -1, it depends..
shuffle($range);
for($i = 0; $i < $numWords; $i++) {
$r = array_pop($range);
$wordList[$r] = $finalArray[$i];
}
I do not know why you want it.. may be it is easier to shuffle($finalArray);??
So ideally "abcdefghi" in some random order.
$letters = str_split('abcdefghi');
shuffle($letters);
var_dump($letters);
ps: if you have hardcoded array $wordList and you want to take first $n elements of it and shuffle then (if this is not an associative array and you do not care about the keys)
$newArray = array_slice($wordList, 0, $n);
shuffle($newArray);
var_dump($newArray);
You can try array_rand and unset
For example:
$array = array('one','two','free','four','five');
$count = count($array);
for($i=0;$i<$count;$i++)
{
$b = array_rand($array);
echo $array[$b].'<br />';
unset($array[$b]);
}
after you have brought the data in the array, you purify and simultaneously removing the memory array
Ok... I have NO idea why you are trying to use so many variables with this.
I certainly, have no clue what you were using $arrayTrack for.
There is a very good chance I am mis-understanding all of this though.
<?php
$numWords=10;
$wordList=array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');
$finalArray=array();
for ($i=0; $i<$numWords; $i++) {
start:
$r=rand(0,$numWords);
$wordChoice=$wordList[$r];
foreach ($finalArray as $word) {
if ($word==$wordChoice) goto start;
}
$finalArray[]=$wordChoice;
}
echo "Result: ".implode(',',$finalArray)."\n";

Return values of an array from behind (last element) php

say I have an array
$test_backwards=array("something1","something2","something3");
this is just a testing example and it's important to note that values will be added dinamically in my final array. so is it possible to dynamically return values from behind, namely starting from the last element?
something like this but backwards
for($i=0;$i<count($test_backwards);$i++) {
echo $test_backwards.'<br>';
}
Just start at the end and decrement your index:
for ($i = count($test_backwards) - 1; $i >= 0; $i--) {
echo $test_backwards[$i].'<br>';
}
or use array_reverse() (slower):
$test_backwards = array_reverse($test_backwards);
for ($i = 0; $i < count($test_backwards); $i++) {
echo $test_backwards[$i].'<br>';
}
You can also use array_pop(), if you do not need to keep this array. Or you can assign it to a temp array and then array_pop it, it will get and delete value from last.
$temp = $test_backwards;
while(($item = array_pop($temp)) !== NULL ) {
echo $item;
}

How to sort it better?

I have 2 arrays, both are multidimensional with same number of elements and same values, which are on different positions (those values are actually ID-s from my database, so one ID appears only once). How can I sort second array with values which are in first array?
For example - if first array looks like:
$array1[0][0] = 1;
$array1[0][x] = it doesn't matter what's here
$array1[1][0] = 4;
$array1[1][x] = it doesn't matter what's here
$array1[2][0] = 3;
$array1[2][x] = it doesn't matter what's here
...
how to sort second array so it would have same values as array1 on indexes [0][0], [1][0], [2][0], etc.
How I could solve problem is:
$i=0
while ($i < (count($array1)-2)){ // * check down
$find_id = $array1[$i][0];
// here I need to search for index of that ID in other array
$position = give_index($find_id, $array2);
// swapping positions
$temp = array2[$i][0];
$array2[$i][0] = $array2[$position][0];
$array2[$position][0] = $temp;
// increasing counter
i++;
}
function give_index($needle, $haystack){
for ($j = 0, $l = count($haystack); $j < $l; ++$j) {
if (in_array($needle, $haystack[$j][0])) return $j;
}
return false;
}
*There is only -2 because indexes start from 0 and also for the last element you don't need to check since it would be automatically sorted by last iteration of while-loop.
I don't find this solution good as I think that this is quite simple issue (maybe it's not even correct). Is there easier way in PHP that I'm missing?
This is the most efficient way I can think of:
function swap(&$a, &$b) {
$t = $a;
$a = $b;
$b = $t;
}
function find_index($id, $array, $from = 0) {
$index = false;
for ($i = $from, $c = count($array); $i < $c; $i++) {
if ($array[$i][0] == $id) {
$index = $i;
break;
}
}
return $index;
}
for ($i = 0, $c = count($array1); $i < ($c - 2); $i++) {
if ($array1[$i][0] != $array2[$i][0]) {
$fi = find_index($array1[$i][0], $array2, $i);
swap($array2[$i][0], $array2[$fi][0]);
}
}
What changes from yours?
I've defined a swap() function in order to swap any variable. That doesn't cost anything and makes everything look nicer. Also you can reuse that function later if you need to.
In the find_index (give_index in your code) we stop the loop once we find the correct index. Also we avoid the cost of an in_array function call.
We modified the find_index function to start only from the part of the array we haven't checked yet. Leading to a way more efficient way of scan the array.
In the for loop (a while loop was just wrong there) we stored the count of the array once, avoiding multiple calls.
Also we swap the $array2 values only if they are in the wrong place.
Other improvements
If you know anything else of the $array2 array you can make this even more performant. For example if you know that indexes are alternated like in $array1 you can change the main for loop from:
for ($i = 0, $c = count($array1); $i < ($c - 2); $i++) {
to
for ($i = 0, $c = count($array1); $i < ($c - 2); $i+2) {
(notice the $i+2 at the end) And you could do that in the find_index function as well.
Look into usort (http://php.net/manual/en/function.usort.php).
It provides a simple way to sort arrays using a user provided comparison function.

Reorder elements in array evenly

Say i have:
$array = (1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
What I'm trying to achive is to reorder elements evenly in it.
PHP's function shuffle() don't fits here, because i want some distance between same digits. So 1's has to be somewhere in the beginning of array, in the middle and in the end too.
I google about Fisher-Yates_shuffle algorithm, but it seems to work exactly like shuffle().
Thanks in advance!
I think this is close to what you ask: A constant, reasonably even distribution of the items in an array.
// The input array. 0s are regarded as blanks.
$array = array(1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0);
// Count the times each item occurs. PHP will probably have a function for that, but I don't know.
$counter = array();
foreach ($array as $item)
{
// Zeros are infill. Don't process them now, only process the other numbers and
// the zeros will occupy the remaining space.
if ($item === 0)
continue;
if (!array_key_exists($item, $counter))
$counter[$item] = 0;
$counter[$item]++;
}
// Reverse sort by quantity. This results in the best distribution.
arsort($counter);
// Pre-fill a new array with zeros.
$resultCount = count($array);
$result = array_fill(0, $resultCount, 0);
// Distribute the items in the array, depending on the number of times they occur.
foreach ($counter as $item => $count)
{
// Determine the division for this item, based on its count.
$step = $resultCount / $count;
// Add the item the right number of times.
for ($i = 0; $i < $count; $i++)
{
// Start with the index closest to the preferred one (based on the calculated step).
$index = 0;
$startIndex = (int)($step * $i);
// Count up until a right index is found.
for ($index = $startIndex; $index < $resultCount; $index++)
{
if ($result[$index] === 0)
{
$result[$index] = $item;
break;
}
}
// If no proper index was found, count fown from the starting index.
if ($index === $resultCount)
{
for ($index = $startIndex; $index >= 0; $index--)
{
if ($result[$index] === 0)
{
$result[$index] = $item;
break;
}
}
}
// Still no proper index found, that shouldn't be possible. There's always room.
if ($index === -1)
{
throw new Exception('This cannot not happen');
}
}
}
var_dump($result);
For array:
1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
It returns:
3,2,1,0,3,0,0,0,3,0,2,1,3,0,0,0,3,0,0,0,0,3,2,1,0,3,0,0,0,3,0,2,1,3,0,0,0,3,0,0,0,0
For array:
1,1,1,1,2,2,2,2,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,0,0
It returns:
4,4,3,4,3,4,2,4,3,4,2,4,3,4,1,4,3,4,1,4,0,4,4,3,4,3,4,2,4,3,4,2,4,3,4,1,4,3,4,1,4,0
Which I think is a neat distribution. Thanks to datdo for the idea of sorting the intermediate array.

Categories