If is in array? - php

Really struggling with this one:
I have an existing foreach, containing an if loop looking for specific values. But I also have an array containing values which if found should have the same action taken as the specific values:
Here I loop through and when values are between 5 and 9, I take the value of $datacolvalue and add it to another array as an integer rounded to 2 decimal places.
Otherwise, add it as a string, untouched.
$data_row = array();
$count = 1;
foreach ($row->COLUMN as $datacolvalue){
if($count > 4 && $count < 10)
$data_row[] = round((float)$datacolvalue, 2);
else
$data_row[] = (string)$datacolvalue;
$count++;
}
What I want to do, is is do the same thing $data_row[] = round((float)$datacolvalue, 2); if the value of $count is in a static array named $array_to_round which looks like this (values are different each time the php is run:
array(12,34,56,78);
I though about adding a foreach inside the "else" condition but I cannot get my head around it. Is a for/while/loop the answer?
In a nutshell, for each $datacolvalue, if $count is (> 4) and (< 10) OR is present in the $array_to_round array place in array as int and round, otherwise, place it as a string.

Use in_array to check if the value exists in the other array, then add it as necessary,
I would also store the rounded value if you plan to use it as a check and a setter.

Related

Replace repeating value with zero in PHP string

The following is the code
<?php
$id ="202883-202882-202884-0";
$str = implode('-',array_unique(explode('-', $id)));
echo $str;
?>
The result is
202883-202882-202884-0
for $id ="202883-202882-202882-0";, result is 202883-202882-0
I would like to replace the duplicate value with zero, so that the result should be like 202883-202882-0-0, not just remove it.
and for $id ="202883-0-0-0";, result should be 202883-0-0-0. zero should not be replaced, repeating zeros are allowed.
How can I archive that?
More info:
I want to replace every duplicate numbers. Because this is for a product comparison website. There will be only maximum 4 numbers. each will be either a 6 digit number or single digit zero. all zero means no product was selected. one 6 digit number and 3 zero means, one product selected and 3 blank.
Each 6 digit number will collect data from database, I dont want to allow users to enter same number multiple times (will happen only if the number is add with the URL manually.).
Update: I understand that my question was not clear, may be my English is poor.
Here is more explanation, this function is for a smartphone comparison website.
The URL format is sitename.com/compare.html?id=202883-202882-202889-202888.
All three numbers are different smartphones(their database product ID).
I dont want to let users to type in the same product ID like id=202883-202882-202882-202888. It will not display two 202882 results in the website, but it will cause some small issues. The URL will be same without change, but the internal PHP code should consider it as id=202883-202882-202888-0.
The duplicates should be replaced as zero and added to the end.
There will be only 4 numbers separated by "-".
The following examples might clear the cloud!
if pid=202883-202882-202889-202888 the result should be 202883-202882-202889-202888
if pid=202883-202883-202883-202888 the result should be 202888-0-0-0
if pid=202883-202882-202883-202888 the result should be 202883-202882-202888-0
if pid=202882-202882-202882-202882 the result should be 202882-0-0-0
I want to allow only either 6 digit numbers or single digit zero through the string.
if pid=rgfsdg-fgsdfr4354-202883-0 the result should be 202883-0-0-0
if pid=fasdfasd-asdfads-adsfds-dasfad the result should be 0-0-0-0
if pid=4354-45882-445202882-202882 the result should be 202882-0-0-0
It is too complicated for me create, I know there are bright minds out there who can do it much more efficiently than I can.
You can do a array_unique (preserves key), then fill the gaps with 0. Sort by key and you are done :)
+ on arrays will unify the arrays but prioritizes the one on the left.
Code
$input = "0-1-1-3-1-1-3-5-0";
$array = explode('-', $input);
$result = array_unique($array) + array_fill(0, count($array), 0);
ksort($result);
var_dump(implode('-',$result));
Code (v2 - suggested by mickmackusa) - shorter and easier to understand
Fill an array of the size of the input array. And replace by leftover values from array_unique. No ksort needed. 0s will be replaced at the preserved keys of array_unique.
$input = "0-1-1-3-1-1-3-5-0";
$array = explode('-', $input);
$result = array_replace(array_fill(0, count($array), 0), array_unique($array));
var_export($result);
Working example.
Output
string(17) "0-1-0-3-0-0-0-5-0"
Working example.
references
ksort - sort by key
array_fill - generate an array filled with 0 of a certain length
This is another way to do it.
$id = "202883-202882-202882-0-234567-2-2-45435";
From the String you explode the string into an array based on the delimiter which in this case is '-'/
$id_array = explode('-', $id);
Then we can loop through the array and for every unique entry we find, we can store it in another array. Thus we are building an array as we search through the array.
$id_array_temp = [];
// Loop through the array
foreach ($id_array as $value) {
if ( in_array($value, $id_array_temp)) {
// If the entry exists, replace it with a 0
$id_array_temp[] = 0;
} else {
// If the entry does not exist, save the value so we can inspect it on the next loop.
$id_array_temp[] = $value;
}
}
At the end of this operation we will have an array of unique values with any duplicates replaced with a 0.
To recreate the string, we can use implode...
$str = implode('-', $id_array_temp);
echo $str;
Refactoring this, using a ternary to replace the If,else...
$id_array = explode('-', $id);
$id_array_temp = [];
foreach ($id_array as $value) {
$id_array_temp[] = in_array($value, $id_array_temp) ? 0 : $value;
}
$str = implode('-', $id_array_temp);
echo $str;
Output is
202883-202882-0-0-234567-2-0-45435
This appears to be a classic XY Problem.
The essential actions only need to be:
Separate the substrings in the hyphen delimited string.
Validate that the characters in each substring are in the correct format AND are unique to the set.
Only take meaningful action on qualifying value.
You see, there is no benefit to replacing/sanitizing anything when you only really need to validate the input data. Adding zeros to your input just creates more work later.
In short, you should use a direct approach similar to this flow:
if (!empty($_GET['id'])) {
$ids = array_unique(explode('-', $_GET['id']));
foreach ($ids as $id) {
if (ctype_digit($id) && strlen($id) === 6) {
// or: if (preg_match('~^\d{6}$~', $id)) {
takeYourNecessaryAction($id);
}
}
}

How to loop though an array and create an array, in the loop, containing values from the original array?

Hey everyone I have a question about arrays and loops in PHP.
For a game I'm making, I need to write a function that generates a stack of crystal_id's based on a given size and ratio.
The ratio is the ratio between black crystals and different colored crystal (so a ratio of 0,25 (1:4) and a stack of 50 would yield a stack with 40 black crystals and 10 colored crystals).
I have all the math to calculate the amount of crystals per color and stuff figured out, but I can't figure out how to create an array with the right amount of colored crystals where each color is represented equally.
For reference, the array the code gets to choose from is a variable called $crystals_array, which is an array filled with integers where each integer represents a different colored crystal (e.g. [2,3,4,5,6]).
In the above case we have 5 different colored crystals and we needed a total of 10 colored crystals where each crystal is represented equally. So I need to create an array that looks a little something like this:
[2,2,3,3,4,4,5,5,6,6].
The code I have so far is this:
for($i = 0; $i <= count($amount_crystals_color) - 1; $i++)
{
$array = array_fill(0, $amount_crystals_per_color_stack, $crystals_array[$i]);
$i++;
}
Using the above example $amount_crystals_per_color_stack is equal to 2 and amount_crystals_color is equal to 5.
When executing this code it outputs an array: [2,2] which is how many 2's we need, but I can't figure out how to add the remaining crystals to this array.
Can anyone help me out?
Your code has several problems and i will address each of them individually.
Using the for loop to iterate over an array
You are using a for loop in your code, that has the following loop head:
for($i = 0; $i <= count($crystals_array) - 1; $i++)
The loop had consists of three parts, each separated by a semicolon (;).
The first part $i = 0 is the initialization part. Here you initialize a variable, that is later used as an iterator, which shall change in each loop iteration. You could name this the start point as well.
The second part $i <= count($crystals_array) - 1 is the condition part. A condition is formed, that shall express for how long the loop shall iterate. As long as the expression evaluates as true, the loop will run again. Therefore this condition is evaluated at the start of each iteration. As soon as the condition evaluates as false the loop will end. Therefore this part can be named endpoint as well.
The third part $i++ is the step size. This is executed at the end of each iteration and determines how the iterator (the variable you defined in the first part) shall change. $i++ is in this context equal to $i = $i + 1 and represents a step size of 1. Therefore the variable $i gets increased by 1 for each run of the loop.
This said, you can improve and fix your code regarding the for loop with two changes:
Save functions, that are executed in your condition part into an variable, if they return a constant result for each iteration. You use the count() function, which will then count your array again for each iteration of the for loop. By saving it in a variable $count = count($crystals_array); before the for loop and changing the condition to $i < $count, the function is only called once and your code gets faster.
Do not change the iterator variable $i outside of your loop header. This is really bad code style. You added the line $i++; to the end of your loop, but that is already done in the step size part of the for header. Because that is executed at the and of each iteration as well you increased the step size to two, meaning that you only run the for loop with $i = 0, $i = 2 and $i = 4 instead of for each element.
For your code the usage of the $i iterator is only to address the elements of the initial array. Even though you should understand the for loop for the future, you should use a foreach loop for this case instead. The following code would be equivalent to your for loop.
//This code still contains another major bug and is jsut a partial improvvement
foreach($crystals_array as $crystal) {
$array = array_fill(0, $amount_crystals_per_color_stack, $crystal);
}
As you can see, you neither need to worry about counting the initial array, nor in which index the current value is. Instead the variable $crystal will automatically contain the next element for each iteration.
Appending elements to an array
You used the following line to save the newly generated elements in your array:
$array = array_fill(0, $amount_crystals_per_color_stack, $crystal);
If you look closely, you use a standard assignment with $array = at the beginning of your line. This means, that (like with each variable assignment) the previous value of the variable gets overwritten by the new value provided from the right side of the assignment. What you do not want yet is to overwrite the array, but to append something to it.
This can be done by adding two squared brackets to the end of the variable name: $array[] = .... Now, if the variable $array is really an array, what ever value is on the right side of the assignment will be appended to the array, instead of overwriting it.
Managing result types the right way
The following line still contains a major problem:
$array[] = array_fill(0, $amount_crystals_per_color_stack, $crystal);
The result type of array_fill() is an array itself. By appending it to the previous array, you would get the following structure:
$array = [
[2, 2],
[3, 3],
[4, 4],
[5, 5],
[6, 6],
];
As you can see, the code did exactly what it should but not what you wanted. Each result (array) was appended to the array. The result is therefore an array or arrays (or a multidimensional array). What you want instead, is that the values of the result are appended to the existing array.
PHP offers a function for that, named array_merge(). This function takes all elements for one (or more) array(s) and appends them to the end of the first array, that was given to the function. You can use it as followed:
$newCrystals = array_fill(0, $amount_crystals_per_color_stack, $crystal);
$array = array_merge($array, $newCrystals);
As you can see the latter line contains a normal assignment again. ($array =) This is because array_merge() does not modify the first array given to it, but creates a new array with the merged fields. Therefore the new array contains all values from the old one and it is safe to overwrite the old one with it.
The complete code would therefore be:
$array = [];
foreach($crystals_array as $crystal) {
$newCrystals = array_fill(0, $amount_crystals_per_color_stack, $crystal);
$array = array_merge($array, $newCrystals);
}
As I understood the problem
$crystals_array = [2,3,4,5,6];
$amount_crystals_per_color_stack = 2;
$res = [];
foreach($crystals_array as $v) {
// repeat each item from array $amount_crystals_per_color_stack times
$res = array_merge($res, array_fill(0, $amount_crystals_per_color_stack, $v));
}
print_r($res); // [2,2,3,3,4,4,5,5,6,6]
You need to merge your array at each iteration of the loop (repl online) or you lose the result each time.
Like:
$array = array();
for($i = 0; $i < count($amount_crystals_color); $i++)
{
$array = array_merge($array, array_fill(0, $amount_crystals_per_color_stack, $crystals_array[$i]);
}
Also you don't need the $i++ in the loop, because it iterate twice otherwise, and you don't need count(..)-1 if the condition is < instead of <=.
You could use simple foreach() to achieve this-
$amount_crystals_per_color_stack = 2;
$array = [2,3,4,5,6];
$result = array();
foreach($array as $a){
for($i=1;$i<=$amount_crystals_per_color_stack;$i++){
array_push($result, $a);
}
}
print_r($result);

count the elements in array except of specific element

For example i have an array like this [-1,-1,-1,-1,-1,-1,2.5,-1,-1,8.3]
I want to count the element which are not -1 like except of the -1. Is there any function in php to get this ? One approach which i think is
Count the total array - count of the -1 in the array.
how can we achieve this.
P.S : please provide me a comment why this question deserve negative vote.
Like #MarkBaker said, PHP doesn't have a convenient function for every single problem, however, you could make one yourself like this:
$arr = [-1,-1,-1,-1,-1,-1,2.5,-1,-1,8.3];
function countExcludingValuesOfNegativeOne ($arr) {
return count($arr) - array_count_values(array_map('strval', $arr))['-1'];
}
echo countExcludingValuesOfNegativeOne($arr); // Outputs `2`.
It just counts the whole array, then subtracts the number of negative 1s in the array. Because PHP throws an error if any element in the array isn't a string 1 when using array_count_values, I've just converted all elements of the array to a string using array_map('strval', $arr) 2
you can use a for loop counter skipping for a specific number.
function skip_counter($array,$skip){
$counter = 0;
foreach ($array as $value) {
if ($value != $skip) $counter++;
}
return $counter;
}
then you call skip_counter($your_list, $number_to_be_skipped);

How do I compare and unset an object in an array against the other objects in the array?

What I'm trying to do: Compare an array value which is an object with other objects in the array. If certain object properties in the needle (the object I'm comparing against the other objects) match the same properties in another object in the haystack, then merge certain properties from the needle to the matched object in the haystack and unset the needle.
Some pseudo-code:
<?php
$haystack = array($obj1, $obj2, $obj3);
if(!empty($haystack)){
for($x=0;$x<count($haystack);$x++){
if($haystack[$x]->prop1 == $haystack[$x+1]->prop1 && $haystack[$x]->prop2 == $haystack[$x+1]->prop2){
$haystack[$x]->combined1 = $haystack[$x]->prop1.','.$haystack[$x+1]->prop1;
unset($haystack[$x+1]);
}
}
}
?>
Expanding a tad on my comment:
You'll have to change unset($haystack[$x+1]); to unset($haystack[++$x]);
If, say indexes 0 and 1 are equal, your current loop unsets $haystack[1], keeping the value of $i at 0, the loop then continues to increment $i with 1 (to 1 in this case), and executes the comparison with $haystack[1], which has just been unset.
Actually incrementing $i means that, at the end of the loop $i will be 1, instead of 0. That will be incremented once more, and the next time around, the loop will compare indexes 2 and 3.
for ($x = 0, $max = count($haystack);$x <$max;++$x)
{
if ($haystack[$x] == $haystack[$x+1])
{//only increment if you unset!
unset($haystack[++$x]);
}
}
That should do the trick. If you want to compare all elements in the given array, then you'll have to nest two loops:
for ($x = 0, $max = count($haystack);$x <$max;++$x, $max =count($haystack))
{//re-assign $max after inner loop completes, and reset keys
for ($j = $x+1;$j<$max;++$j)
{
if ($haystack[$x] == $haystack[$j])
{//No need to increment again
unset($haystack[$j]);
}
}
$haystack = array_values($haystack);//this resets the keys
}
That should solve your problem
Solution
for($x=0;$x<count($haystack);$x++){
for($y=0;$y<=count($haystack);$y++){
if($haystack[$x]->prop1 == $haystack[$y]->prop1 && $haystack[$x]->prop2 == $haystack[$y]->prop2){
if($haystack[$x]->prop3 == $haystack[$y]->prop3){
continue;
}else{
$haystack[$x]->prop1 .= $haystack[$y]->prop1;
unset($haystack[$y]);
}
}
}
}
In retrospect, this was a fairly easy problem to solve. The issue was trying to mentally keep the iterators straight and knowing how many were needed. In order for this to work without copying the array or using an intermediary, you just need to instantiate two iterators to iterate over the array simultaneously so you can compare $array[0-n] to $array[0-n] instead of comparing $array[0-n] to $arrayCopy[0-n].

Limit array to 5 items

I have a code that will add a number to an array each time a page is visited. the numbers are stored in a cookie and are retrieved later.
I would like to keep only the 5 most recent numbers in the array.
if the array is full (5 items) and a new number must be added, then the oldest number must be removed and the most recent items must be kept
here's what i have:
$lastviewedarticles = array();
if (isset($_COOKIE["viewed_articles"]) ) {
$lastviewedarticles = unserialize($_COOKIE["viewed_articles"]);
}
if (!in_array($articleid, $lastviewedarticles)){
$lastviewedarticles[] = $articleid;
}
setcookie("viewed_articles", serialize($lastviewedarticles));
array_slice returns a slice of an array
array_slice($array, 0, 5) // return the first five elements
use array_splice and array_unique to get the 5 unique array values
array_splice(array_unique($lastviewedarticles), 0, 5);
First of all i think, you need to obtain array length , then if length > or equal to 5, remove first element , and add element to the end of array.
if (!in_array($articleid, $lastviewedarticles)){
$count = count($lastviewedarticles);
if($count>=5)
array_shift($lastviewedarticles);
$lastviewedarticles[] = $articleid;
}
Use a counter to access the array, increment it in every call and use the modulus operation to write into the array. If your counter has to persist over several calls you have to store it in a session variable or a cookie.
Assuming that $i holds your counter variable this would look like
if (!in_array($articleid, $lastviewedarticles)){
$lastviewedarticles[$i%5] = $articleid;
$i++;
}
The result is a primitive ring buffer that will always contain the last 5 values.

Categories