I am uploading a CSV file/report with 10 columns but at the end of the CSV file there are a few lines that just give details about the report like
Generated By: XXX
Company Name
Report Run # 2019-03-14
When I load the array, the keys are just numeric (from 0-9) but I wanted to make it be an associative array based on the column headers. Unfortunately this will not work for the last few lines since the array dimensions are different (1 vs 10)
Here is my code:
$csv = array_map('str_getcsv', file($_FILES['file']['tmp_name']));
array_walk($csv, function(&$a) use ($csv) {
if(count($csv[0]) != count($a)) {
$a = null; // Remove the array
} else {
$a = array_combine($csv[0], $a);
}
});
array_shift($csv); # remove column header
When I do $a = null; it sort of 'deletes it' by replacing it with NULL. When I iterate through the arrays I do if(is_null($row)) continue; to ignore the NULL element. Is there a way to actually delete the array?
I think it's more straightforward without array_walk. array_walk is just going to apply the function to every member of the array. Setting it to null doesn't mean it's gone, it just has a null value as you've seen. If you really want it gone, you need to unset it. Just refer to $csv by key and unset the ones you don't want.
$keys = array_shift($csv);
$expected_count = count($keys);
foreach ($csv as $index => $values) {
if (count($values) == $expected_count) {
$csv[$index] = array_combine($keys, $values);
} else {
unset($csv[$index]);
}
}
Array_filter($csv); afterwards will remove all null/false/0 from your array.
So it may be smarter to write a custom function to remove null only.
Related
Edit1: The problem: I want to convert in php a associative array to a indexed one. So I can return it via json_encode as an array and not as an object. For this I try to fill the missing keys. Here the description:
Got a small problem, I need to transfer a json_encoded array as an array to js. At the moment it returns an Object. I´m working with Angular so I really need an Array. I try to explain it as much as possible.
$arrNew[0][5][0][0][1]["id"] = 1;
//$arrNew[0][0][0][0][1] = "";
//$arrNew[0][1][0][0][1] = "";
//$arrNew[0][2][0][0][1] = "";
//$arrNew[0][3][0][0][1] = "";
//$arrNew[0][4][0][0][1] = "";
$arrNew[0][5][0][0][1]["name"] = 'Test';
var_dump($arrNew);
So if I return it now It returns the second element as object cause of the missing index 0-4 and the 4th element cause of the missing index 0 (associative array -> object)
So if I uncomment the block it works like a charm. Now I have the problem its not every time the element 5 sometime 3, 4 or something else so I build a function which adds them automaticly:
$objSorted = cleanArray($arrNew);
function cleanArray($array){
end($array);
$max = key($array) + 1; //Get the final key as max!
for($i = 0; $i < $max; $i++) {
if(!isset($array[$i])) {
$array[$i] = '';
} else {
end($array[$i]);
$max2 = key($array[$i]) + 1;
for($i2 = 0; $i2 < $max2; $i2++) {
.... same code repeats here for every index
So if I vardump it it returns:
The problem:
On js side its still an object, what I also see is that the elements are not sorted. So I think somehow PHP sees it still as an associative array. Any clue why this happens ? The key is set with the index of the loop and has to be a integer value.
PS: I know reworking it in JS is possible but would have be done nearly on every request with a huge load of loops
If I understand your problem, you create a sparse multidimensional array of objects. Because the arrays have gaps in the keys, json_encode() produces objects on some levels but you need it to produce arrays for all but the most inner level.
The following function fills the missing keys (starting from 0 until the maximum value used as numeric key in an array) on all array levels. It then sorts each array by their keys to make sure json_encode() encodes it as array and not object.
The sorting is needed, otherwise json_encode() generates an object; this behaviour is explained in a note on the json_encode() documentation page:
When encoding an array, if the keys are not a continuous numeric sequence starting from 0, all keys are encoded as strings, and specified explicitly for each key-value pair.
// If $arr has numeric keys (not all keys are tested!) then returns
// an array whose keys are a continuous numeric sequence starting from 0.
// Operate recursively for array values of $arr
function fillKeys(array $arr)
{
// Fill the numeric keys of all values that are arrays
foreach ($arr as $key => $value) {
if (is_array($value)) {
$arr[$key] = fillKeys($value);
}
}
$max = max(array_keys($arr));
// Sloppy detection of numeric keys; it may fail you for mixed type keys!
if (is_int($max)) {
// Fill the missing keys; use NULL as value
$arr = $arr + array_fill(0, $max, NULL);
// Sort by keys to have a continuous sequence
ksort($arr);
}
return $arr;
}
// Some array to test
$arrNew[0][5][0][0][1]["id"] = 1;
$arrNew[0][3][0][2][1]["id"] = 2;
$arrNew[0][5][0][0][1]["name"] = 'Test';
echo("============= Before ==============\n");
echo(json_encode($arrNew)."\n");
$normal = fillKeys($arrNew);
echo("============= After ==============\n");
echo(json_encode($normal)."\n");
The output:
============= Before ==============
[{"5":[[{"1":{"id":1,"name":"Test"}}]],"3":[{"2":{"1":{"id":2}}}]}]
============= After ==============
[[null,null,null,[[null,null,[null,{"id":2}]]],null,[[[null,{"id":1,"name":"Test"}]]]]]
The line $arr = $arr + array_fill(0, $max, NULL); uses NULL as values for the missing keys. This is, I think, the best for the Javascript code that parses the array (you can use if (! arr[0]) to detect the dummy values).
You can use the empty string ('') instead of NULL to get a shorter JSON:
[["","","",[["","",["",{"id":2}]]],"",[[["",{"id":1,"name":"Test"}]]]]]
but it requires slightly longer code on the JS side to detect the dummy values (if (arr[0] != '')).
I have an array that carries a definite number of dimensions so I'm not really looking at something recursive (Unless maybe for maintainability sake in the future). It's a numeric array gotten from the database with each row holding another array. Each of those level 2 arrays contain strings like
var1, var2 , var3
And so on. Note the irregular appearance of commas in the string. So I intend to break the comma delimited string in the third level then log them in the final array but I get an error saying I am supplying an null array. So I want to know why it says the array is null and how I can make it recognise that as a valid array. My code goes below:
function fetch_each($arr) {
$temp = array();
for ($i = 0; $i < count($arr); $i++) {
for ($j = 0; $j < count($arr[$i]); $j++) {
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) {
return array_push($temp, $a, $b);
});
}
}
return $temp;
}
PS: Please don't mark as duplicate. I don't want to copy someone else's code but want to understand why this does not work. Thanks.
You have this problem because $temp is not visible in the function block.
To solve that, you must use the keyword use (variable_name) next to the function definition as in this example :
array_reduce(preg_split("/[\s,]+/", $arr[$i][$j]), function($a, $b) use (&$temp) {
return array_push($temp, $a, $b);
});
Just a remark, $a will contain the result of array_push
Returns:int the new number of elements in the array.
So you can remove it from the array_push() instruction to keep a clean array with only splitted strings
I have 4 arrays and use array_multisort to sort them all at the same time relative to one another.
Problem is, in the first array, there can be empty values and I want to put them at the end, not the beginning.
Example : http://codepad.org/V6TjCsS5
Is there a way to:
Pass a custom function to array_multisort
or
Sort the first array with a custom function then use the result order to sort the other arrays
or
Use a certain argument with array_multisort to achieve what I want
Thank you very much
Unfortunately none of the approaches your propose is possible, which means you have to take another step back and look for alternatives. I am assuming you want a normal ascending sort, with the explicit exception that empty elements (which you be "smallest") need to be considered as "largest".
Option 1: Manually rearrange elements after sorting
Do your array_multisort as usual, and then make the modifications you require:
// $arr1, $arr2 etc have been sorted with array_multisort
while(reset($arr1) == '') {
$k = key($arr1);
unset($arr1[$k]); // remove empty element from beginning of array
$arr1[$k] = ''; // add it to end of array
// and now do the same for $arr2
$v = reset($arr2);
$k = key($arr2);
unset($arr2[$k]);
$arr2[$k] = $v;
// the same for $arr3, etc
}
You can pull out part of the code in a function to make this prettier:
function shift_and_push(&$arr) {
$v = reset($arr);
$k = key($arr);
unset($arr[$k]);
$arr[$k] = $v;
}
Option 2: Condense everything inside one array so you can use usort
The idea here is to pull all your arrays into one so you can specify the comparison function by using usort:
$allArrays = array_map(function() { return func_get_args(); },
$array1, $array2 /* , as many arrays as you want */);
You can now sort:
// writing this as a free function so that it looks presentable
function cmp($row1, $row2) {
// $row1[0] is the item in your first array, etc
if($row1[0] == $row2[0]) {
return 0;
}
else if($row1[0] == '') {
return 1;
}
else if($row2[0] == '') {
return -1;
}
return $row1[0] < $row2[0] ? -1 : 1;
}
usort($allArrays, "cmp");
At this point you are left with an array, each element (row) of which is an array. The first elements of each are what was originally inside $array1, second elements are what was in $array2, etc. By placing those elements inside "rows", we have managed to keep the sort order among all your original arrays synchronized.
The second argument to array_multisort can be the options to the sort. So you could pass SORT_DESC if SORT_ASC is doing the opposite of what you want.
array_multisort($arr, SORT_DESC);
For completeness sake, here are the other options SORT_ASC, SORT_DESC, SORT_REGULAR, SORT_NUMERIC, SORT_STRING. SORT_STRING may be helpful.
Do you actually want the empty elements? Just remove them before sorting:
foreach($array1 as &$v) {
if($v==='')
{
unset($v);
}
}
What is the difference in the following array notations: $arr[$key] = $value and $arr[] = $value, which is a better way ?
function test(){
$key = 0;
$a = array(1,2,3,4,5);
foreach($a as $value){
$a[$key] = $value;
$key++;
}
print_r($a);
}
versus
function test(){
$a = array(1,2,3,4,5);
foreach($a as $value){
$a[] = $value;
}
print_r($a);
}
They are different.
$a[] = 'foo';
Adds an element to the end of the array, creating a new key for it (and increasing the overall size of the array). This is the same as array_push($array, 'foo');
$key = 0;
$a[$key] = 'foo';
Sets the 0 element of the array to foo, it overwrites the value in that location... The overall size of the array stays the same... This is the same as $array = array_slice($array, 0, 1, 'foo'); (but don't use that syntax)...
In your specific case, they are doing 2 different things. The first test function will result in an array array(1,2,3,4,5), whereas the second one will result in array(1,2,3,4,5,1,2,3,4,5). [] always adds new elemements to the end.... [$key] always sets....
$arr[$key] = $value sets a specific key to a specific value.
$arr[] = $value adds a value on to the end of the array.
Neither is "better". They serve completely different roles. This is like comparing writing and drawing. You use a pen for both of them, but which you use depends on the circumstance.
One ($a[$key] = $value) youare specifying the $key where PHP will override that array entry if you use the same key twice.
The other ($a[] = $value) PHP is handling the keys itself and just adding the array entry using the next available key.
The whole thing that you are doing is a bit redundant though, as in the first example you are trying to loop through the array setting its values to itself. In the second example you are duplicating the array.
If you want to append an element to the array I would use
$arr[] = $value;
It is simpler and you get all the information on that line and don't have to know what $key is.
I know there is array_unique function, but I want to remove duplicates. Is there a built-in function or do I have to roll my own.
Example input:
banna, banna, mango, mango, apple
Expected output:
apple
You can use a combination of array_unique, array_diff_assoc and array_diff:
array_diff($arr, array_diff_assoc($arr, array_unique($arr)))
You can use
$singleOccurences = array_keys(
array_filter(
array_count_values(
array('banana', 'mango', 'banana', 'mango', 'apple' )
),
function($val) {
return $val === 1;
}
)
)
See
array_count_values — Counts all the values of an array
array_filter — Filters elements of an array using a callback function
array_keys — Return all the keys or a subset of the keys of an array
callbacks
Just write your own simple foreach loop:
$used = array();
$array = array("banna","banna","mango","mango","apple");
foreach($array as $arrayKey => $arrayValue){
if(isset($used[$arrayValue])){
unset($array[$used[$arrayValue]]);
unset($array[$arrayKey]);
}
$used[$arrayValue] = $arrayKey;
}
var_dump($array); // array(1) { [4]=> string(5) "apple" }
have fun :)
If you want to only leave values in the array that are already unique, rather than select one unique instance of each value, you will indeed have to roll your own. Built in functionality is just there to sanitise value sets, rather than filter.
You want to remove any entries that have duplicates, so that you're left with only the entries that were unique in the list?
Hmm it does sound like something you'll need to roll your own.
There is no existing function; You'll have to do this in two passes, one to count the unique values and one to extract the unique values:
$count = array();
foreach ($values as $value) {
if (array_key_exists($value, $count))
++$count[$value];
else
$count[$value] = 1;
}
$unique = array();
foreach ($count as $value => $count) {
if ($count == 1)
$unique[] = $value;
}
The answer on top looks great, but on a side note: if you ever want to eliminate duplicates but leave the first one, using array_flip twice would be a pretty simple way to do so. array_flip(array_flip(x))
Only partially relevant to this specific question - but I created this function from Gumbo's answer for multi dimensional arrays:
function get_default($array)
{
$default = array_column($array, 'default', 'id');
$array = array_diff($default, array_diff_assoc($default, array_unique($default)));
return key($array);
}
In this example, I had cached statuses and each one other than the default was 0 (the default was 1). I index the default array from the IDs, and then turn it into a string. So to be clear - the returned result of this is the ID of the default status providing it's in the same part of the multi dimensional array and not the key of it
PHP.net http://php.net/manual/en/function.array-unique.php
array array_unique ( array $array [, int $sort_flags = SORT_STRING ] )
Takes an input array and returns a new array without duplicate values.
New solution:
function remove_dupes(array $array){
$ret_array = array();
foreach($array as $key => $val){
if(count(array_keys($val) > 1){
continue;
} else {
$ret_array[$key] = $val;
}
}