I have a csv file that I would like to filter. The output I need would be only to output the lines if the increment is not equal to 2. In the csv file below, I would like to compare the first line with the second line, if the increment is 2, check line 3 vs line 2, and so on. If the increment is not equal to 2, output the line. I'm looking at the 3rd cloumn values
L1,is,2.0,mins,LATE,for,Arrive,at,shop,18:07:46
L1,is,4.0,mins,LATE,for,Arrive,at,shop,18:09:46
L1,is,6.0,mins,LATE,for,Arrive,at,shop,18:11:46
L1,is,8.0,mins,LATE,for,Arrive,at,shop,18:13:46
L1,is,10.0,mins,LATE,for,Arrive,at,shop,18:15:46
L1,is,2.0,mins,LATE,for,Arrive,at,shop,18:19:49
L1,is,4.0,mins,LATE,for,Arrive,at,shop,18:21:49
L1,is,6.0,mins,LATE,for,Arrive,at,shop,18:23:49
L1,is,8.0,mins,LATE,for,Arrive,at,shop,18:25:49
L1,is,10.0,mins,LATE,for,Arrive,at,shop,18:27:49
L1,is,16.2,mins,LATE,for,Arrive,at,shop,18:34:02
L1,is,18.2,mins,LATE,for,Arrive,at,shop,18:36:02
L1,is,20.2,mins,LATE,for,Arrive,at,shop,18:38:02
L1,is,2.0,mins,LATE,for,Arrive,at,bridge,21:45:26
L1,is,4.0,mins,LATE,for,Arrive,at,bridge,21:47:26
L1,is,6.0,mins,LATE,for,Arrive,at,bridge,21:49:26
So only lines 5,10,13 and 16 would output to page.
I'm stuck on this and would appreciate any help or direction on where to look.
Thanks
If your file is not too big, you can load it into memory directly, like this:
$data = array_map(function($row)
{
return explode(',', $row);
}, file('/path/to/file.csv', FILE_IGNORE_NEW_LINES));
$result = [];
$increment = 2;
$delta = 1E-13;
for($i=1; $i<count($data); $i++)
{
if(abs($data[$i][2]-$data[$i-1][2]-$increment)>$delta)
{
$result[$i] = $data[$i];
}
}
-since your column holds floats, safe comparison on equality will be using precision delta.
Your data will be gathered in $result array, so you can output it like
foreach($result as $row)
{
echo(join(',', $row).PHP_EOL);
}
-or, else, do not store rows inside $result array (if you will need them no longer) and use first cycle to output your rows.
Edit:
Sample above will work in PHP>=5.4 For PHP 5.3 you should replace array definition to
$result = array();
and if you have even older PHP version, like 5.2, then callback inside array_map() should be rewritten with using create_function()
Related
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);
I have a foreach loop like below code :
foreach($coupons as $k=>$c ){
//...
}
now, I would like to fetch two values in every loop .
for example :
first loop: 0,1
second loop: 2,3
third loop: 4,5
how can I do ?
Split array into chunks of size 2:
$chunks = array_chunk($coupons, 2);
foreach ($chunks as $chunk) {
if (2 == sizeof($chunk)) {
echo $chunk[0] . ',' . $chunk[1];
} else {
// if last chunk contains one element
echo $chunk[0];
}
}
If you want to preserve keys - use third parameter as true:
$chunks = array_chunk($coupons, 2, true);
print_r($chunks);
Why you don't use for loop like this :
$length = count($collection);
for($i = 0; $i < $length ; i+=2)
{
// Do something
}
First, I'm making the assumption you are not using PHP 7.
It is possible to do this however, it is highly, highly discouraged and will likely result in unexpected behavior within the loop. Writing a standard for-loop as suggested by #Rizier123 would be better.
Assuming you really want to do this, here's how:
Within any loop, PHP keeps an internal pointer to the iterable. You can change this pointer.
foreach($coupons as $k=>$c ){
// $k represents the current element
next($coupons); // move the internal array pointer by 1 space
$nextK = current($coupons);
prev($coupons);
}
For more details, look at the docs for the internal array pointer.
Again, as per the docs for foreach (emphasis mine):
Note: In PHP 5, when foreach first starts executing, the internal array pointer is automatically reset to the first element of the
array. This means that you do not need to call reset() before a
foreach loop. As foreach relies on the internal array pointer in PHP
5, changing it within the loop may lead to unexpected behavior. In PHP
7, foreach does not use the internal array pointer.
Let's assume your array is something like $a below:
$a = [
"a"=>1,
"b"=>2,
"c"=>3,
"d"=>4,
"e"=>5,
"f"=>6,
"g"=>7,
"h"=>8,
"i"=>9
];
$b = array_chunk($a,2,true);
foreach ($b as $key=>$value) {
echo implode(',',$value) . '<br>';
}
First we split array into chunks (the parameter true preserves the keys) and then we do a foreach loop. Thanks to the use of implode(), you do not need a conditional statement.
well this'd be 1 way of doing it:
$keys=array_keys($coupons);
for($i=0;$i<count($keys);++$i){
$current=$coupons[$keys[$i]];
$next=(isset($keys[$i+1])?$coupons[$keys[$i+1]]:NULL);
}
now the current value is in $current and the next value is in $next and the current key is in $keys[$i] and the next key is in $keys[$i+1] , and so on.
I got following situation.
I got a csv file and some data in a mysql database.
I want to load the csv file and compare it to a row in the mysql database.
The csv file got following types of data:
eet003 240
eet003 180
eet003 280
eet299 100
But in the database the data looks like this:
eet003
eet299
So both datas a stored in arrays.
$array1
$array2
And I want to compare the two arrays and save the result in a third array.
$diff = array_diff($array1, $array2)
But I onlny want to compare the first 6 chars of the data. So right now eet003 240 is different from eet003. But for me its the same. I only need eet003 e.g.
How could I archieve that? I now the substr function but array1 is created the follwing way:
$rows = array(); //aray for csv file
$rows = file('pinesite_eet.csv', FILE_IGNORE_NEW_LINES); //write csv in array
I don't know how du cut the string from the csv when it's loaded in the array.
The second array is written from a mysql_query.
Any suggestions on how I could do that?
I think something like this should work:
(Here I just use array_udiff() with a anonymous function where I only use the first part of the file data and compare it with the database data)
<?php
$result = array_udiff($fileData, $dbData, function($a, $b) {
if(strcasecmp(explode(" ", $a)[0], $b) == 0)
return 0;
return strcasecmp(explode(" ", $a)[0], $b);
});
?>
Might be easier to just modify the data. This removes the space and everything after in each element:
$rows = preg_replace('/ .*/', '', $rows);
Then array_diff() should work as expected.
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.
Starting with an array with 10K values. I want to randomly get 1000 values from it and put them into another array.
Right now, I am using a for loop to get the values, but I want to pick 1000 values and not have to loop 1000 times. The array_slice function works, but it doesn't give random values. What is the correct (most efficient) function for this task.
The code right now is
$seedkeys = (...array.....);
for ($i=0; $i<1000; $i++) {
$random = array_rand($seedkeys);
$randseed[$i] = $seedkeys[$random];
}//for close
TIA
Well, there are a few alternatives. I'm not sure which is the fastest since you're dealing with a sizable array, but you may want to try them out:
You can use shuffle, which will randomize the entire array. This will likely have the best performance since you're consuming a significant portion of the array (10%).
shuffle($seedkeys);
$result = array_slice($seedkeys, 0, 1000);
You could use array_rand (as you already said) in the manor that Tom Haigh specifies. This will require copying the keys, so if you're dealing with a significant portion of the source array, this may not be the fastest. (Note the use of array_flip, it's needed to allow the usage of array_intersect_key:
$keys = array_flip(array_rand($seedkeys, 1000));
$result = array_intersect_key($seedkeys, $keys);
If memory is tight, the best solution (besides the MySQL one) would be a loop since it doesn't require arrays to be copied at all. Note that this will be slower, but if the array contains a lot of information, it may offset the slowness by being more memory efficient (since it only ever copies exactly what it returns)...
$result = array();
for ($i = 0; $i < 1000; $i++) {
$result[] = $seedkeys[array_rand($seedkeys)];
}
You could do it in MySQL (assuming that the data for the array starts from MySQL). Be aware this is simple, but not that efficient (See Jan Kneschke's post)...
SELECT * FROM `foo` ORDER BY RAND() LIMIT 1000;
You could use array_rand() to get multiple items ?
$random_keys = array_rand($seedkeys, 1000);
shuffle($random_keys);
This will give you an array of random keys, so to get an array of values you need to do something like this:
$result = array();
foreach ($random_keys as $rand_key) {
$result[] = $seedkeys[$rand_key];
}
You could instead use array_intersect_key():
$result = array_intersect_key($seedkeys, array_flip($random_keys));