PDO arrays and array_unique - php

I'm trying to return a list of course id's from my database with PDO. I can access the data, however, I only want to return an array of unique id's i.e.
This
1,2,2,3,4,4
Becomes
1,2,3,4
I'm using the code below
$sth = $dbh->prepare('SELECT courseId from training');
$sth->execute();
$row = $sth->fetchAll();
$result = array_unique($row);
print_r($result);
But, it returns only 1 id:
Array ( [0] => Array ( [courseId] => 8 [0] => 8 ) )
If I print_r $row I can see all my courseId's
What am I doing wrong?

What am I doing wrong?
You are not reading the friendly manual on array_unique, where you can see this:
Note: Two elements are considered equal if and only if (string) $elem1
=== (string) $elem2. In words: when the string representation is the same. The first element will be used.
Since $result contains arrays, and the string representation of each of them is the same ("Array" IIRC) then all the elements compare "equal" and only the first is left.
How to solve the problem: Several ways.
One would be to let the database do it for you with SELECT DISTINCT.
Another would be to go from an array of arrays to an array of just ids, and then array_unique will work just fine. This can be simply done with a foreach loop, or (probably better) with array_map, e.g:
$ids = array_map(function($el) { return $el['courseId']; }, $row);
$unique = array_unique($ids);

Perhaps you would be better off by doing this query:
SELECT DISTINCT courseId FROM training
That way the data coming from the database is already unique and there is no need to use array_unique.

Its the comaprison array_unique is using. I would just do this in the query by using DISTINCT.

Related

Removing duplicates from merged array

I've been doing here some name processor, and I've run into small, kind of noob-ish problem.
I have CSV file with names and status, filters them only by 'Cool Ones" status, then i'm querying SQL, and getting another list of names that i have entered manually.
So here is code example, where i'm taking CSV file, filter, querying SQL, then it creates array, merges it and sorts alphabetically.
$nameFile = "names/$eid.csv";
$content = array_map('str_getcsv', file($nameFile));
$filteredData = array_filter($content, function($v){
return $v['1'] === 'Cool Ones'; },ARRAY_FILTER_USE_BOTH); //because in this file there are also 'Not Cool Ones'
$freePeople = array();
$sth = $DBcon->prepare("SELECT guestName, guestType FROM guestList WHERE forEvent = '$eid' ORDER BY 'guestName'");
$sth->execute();
$result2 = $sth->fetchAll(PDO::FETCH_NUM);
$listNames = array();
foreach($result2 as $row) {
$listNames[] = $row['0'];
$freeGuestName = $row['0'];
$freeGuestType = $row['1'];
}
$merged = array_merge($filteredData, $result2);
$sortedGuests = usort($merged, "sortGuestNames");
so my problem lies, that when outputing array, I'm getting duplicate results,
[50] => Array
(
[0] => John Down
[1] => Best Ones
)
[51] => Array
(
[0] => John Down
[1] => Cool Ones
)
Dunno what's next - i want that if my queried name is same as in this first CSV file, then hide this one, and show mine.
i was trying to unset key with
foreach($merged[$i]['0'] as $key => $value) {
if (in_array($merged[$i]['0'], $value)) {
unset($merged[$i]['0'][$key]);
}
}
but no luck, still outputing duplicates.
You can suggest better approach.
I've thought - maybe open CSV, query SQL and find my manual names - look up in opened CSV fields, append my status there, merge and push them to SQL database or new CSV file, where it could be outputted.
Thanks a lot!
A few things,
The thing we need to do is merge both arrays, but control which one overwrites the other. I am not sure if what you have does that now (in a reliable way) but one way to do that is to build 2 arrays. Both with the same structure, and the key as your unique field so we want this:
$csv = ['John Down' => ['John Down','Best Ones']];
$db = ['John Down' => ['John Down','Cool Ones']];
Then when we do array merge, the second argument will overwrite the first. So if we do
$csv = ['John Down' => ['John Down','Best Ones']];
$db = ['John Down' => ['John Down','Cool Ones']];
print_r(array_merge($csv, $db));
echo "\n";
print_r(array_merge($db, $csv));
Output:
// print_r(array_merge($csv, $db));
Array
(
[John Down] => Array
(
[0] => John Down
[1] => Cool Ones
)
)
//print_r(array_merge($db, $csv))
Array
(
[John Down] => Array
(
[0] => John Down
[1] => Best Ones
)
)
Sandbox
As you can see we can control which array is overwritten by the order we send them to array_merge in. The second one (or right one) overwrites the one to the left. So simply it reads from left to right.
So now what's the easiest way to get that structure from the DB? In PDO we can use FETCH_GROUP which takes the first column in the query and uses it as the top level key.
$sth = $DBcon->prepare("SELECT guestName, guestType FROM guestList WHERE forEvent = :eid GROUP BY guestName ORDER BY guestName");
//-- add `GROUP BY guestName` we don't want duplicates anyway
//-- no quotes see: ... ORDER BY 'guestName');
//-- use prepared statements
$sth->execute(['eid'=>$eid]);
$result2 = $sth->fetchAll(PDO::FETCH_NUM);
$result2 = array_column($result2, null, 0);
For the CSV you can build it that way when you read the file (by adding the key) and using fgetcsv or you can use this trick (also used above):
$csv = [['John Down','Best Ones']];
print_r(array_column($csv, null, 0));
Output
Array
(
[John Down] => Array
(
[0] => John Down
[1] => Best Ones
)
)
Sandbox
Which should give you basically what we need, then it's a simple matter of using array_merge.
One thing to mention is if your DB or CSV are not unique, you'll get some duplicate removal there too, you may have to account for.
Removing duplicates is fine, but you want to make sure to remove the correct duplicates in a repeatable and robust way. Using array_merge we can control that no mater the order the rows come in from the DB and the file.
Summery
So if we put this all together, this is all you should need:
$nameFile = "names/$eid.csv";
$content = array_map('str_getcsv', file($nameFile));
$filteredData = array_filter($content, function($v){
return $v['1'] === 'Cool Ones';
},ARRAY_FILTER_USE_BOTH); //because in this file there are also 'Not Cool Ones'
$sth = $DBcon->prepare("SELECT guestName, guestType FROM guestList WHERE forEvent = :eid GROUP BY guestName ORDER BY guestName");
$sth->execute(['eid'=>$eid]);
$result2 = $sth->fetchAll(PDO::FETCH_NUM);
$listNames = array_column($result2, 0);
$merged = array_merge(array_column($filteredData, null, 0), array_column($result2, null, 0));
$sortedGuests = usort($merged, "sortGuestNames");
So instead of adding code in patching over an issue, we went to the root cause and fixed it there and reduced the code by a few lines. This will work provided your CSV is in the correct format. guestName, guestType
Cheers!
http://php.net/manual/en/function.array-column.php
array_column ( array $input , mixed $column_key [, mixed $index_key = NULL ] ) : array
array_column() returns the values from a single column of the input, identified by the column_key. Optionally, an index_key may be provided to index the values in the returned array by the values from the index_key column of the input array.
input A multi-dimensional array or an array of objects from which to pull a column of values from. If an array of objects is provided, then public properties can be directly pulled. In order for protected or private properties to be pulled, the class must implement both the __get() and __isset() magic methods.
column_key The column of values to return. This value may be an integer key of the column you wish to retrieve, or it may be a string key name for an associative array or property name. It may also be NULL to return complete arrays or objects (this is useful together with index_key to reindex the array).
index_key The column to use as the index/keys for the returned array. This value may be the integer key of the column, or it may be the string key name. The value is cast as usual for array keys (however, objects supporting conversion to string are also allowed).
Assuming that you need unique user name, following is the solution.
Create a new blank users array.
Loop over the users array.
Append the users to new users array.
The key should be user name.
Hence, every time the same user comes, he will overwrite the previous one, removing duplicate.
Code:
$users = [
['John Down', 'Best Ones'],
['John Down', 'Cool Ones']
];
$newUsers = [];
if (! empty($users)) {
foreach ($users as $user) {
$newUsers[$user[0]] = $user[1];
}
}
echo '<pre>';print_r($newUsers);echo '</pre>';
// Output:
Array
(
[John Down] => Cool Ones
)
I solved my case:
I removed second key from merged array, then unserialize it, and mapped only unique ones! Everything is now working!
$input = array_map("unserialize", array_unique(array_map("serialize", $merged)));
Sometimes i really enjoy asking help for you, because it makes me think! To think deeper than ussual.

'vertically join' php arrays

I'm having trouble finding the right function to rearrange my multidimensional-array. I will explain using an example.
so i have three different arrays, let's say:
$ID (containing n numbers)
$Name (containing n names)
$Explanation (containing n explanations)
I have tried array($id, $name, $explanation), which returns a new array, containing the above three arrays as they were before. However I would like to have my new array in the following way:
array(
array (1[0], football[0], played on grass[0])
array (2[1], swimming[1], played in the water[1])
array (3[2], diving[2], played under water[2])
....
array (n+1[n], basketball[n], played indoors[n])
)
I would like my array this way so it can be processed more easily into my database. Thanks in advance!
I assume that all the three arrays are equal in length say $arr1,$arr2,$arr3, Try:
$result = array();
foreach($arr1 as $key=>$val){
$result[] = array($val,$arr2[$key],$arr3[$key]);
}
Then for re-indexing result array starting from index 1 you can try like,
$result = array_unshift($result,null);
unset($result[0]);

Ranking within a PHP array

I have a seemingly simple ranking-type problem associated with php arrays, unfortunately after much research it has defeated me:
I have a simple array where the keys are names of people and the values are just associated numbers:
$myArray = Array("David"=>36, "James"=>24, "Sarah"=>70, "Mary"=>55);
Here’s the challenge: Given a name, what is their rank within the array? For example: Sarah=rank1; It seems simple because I figured I could just sort the array by the values then loop though to the required name to get the rank. However, weirdly when I sort the array it just unhelpfully returns 1!
print_r(asort($myArray)) = 1 (??)
I suppose I could put the array in an MySQL table but that seems a bit heavy handed. Is anyone aware of a php solution? Where am I going wrong with the sort? I've read the documentation here and it seems asort is the appropriate function (preserves association and sorts on values).
Thanks
The Grinch
(Edited - works now)
Kind of ugly but this should work:
arsort($origArr);
$rankedArr = array_keys($origArr);
foreach ($rankedArr as $rank => $person) {
if ($person == 'Sarah') {
echo $rank + 1;
break;
}
}
What you're doing is first sorting by values, then you're dropping those values and just getting an indexed list of people. Their key value + 1 is their rank. (because first is 0, right?)
EDIT2 - slightly cleaner:
arsort($origArr);
$rankedArr = array_keys($origArr);
$finalRanks = array_flip($rankedArr);
$rank = $finalRanks['Sarah'] + 1;
:-)
asort function returns a boolean and sort the given array as a reference
var_dump(asort($myArray)) = bool(true)
If you print_r($myArray) after this previous line, you'll get your sorted array in $myArray
EDIT: Re-read.
Try doing this to get your ranking numerously:
<?php
/* asort = Lower num to Upper */
asort($myArray);
/* arsort = Upper one to lower */
// arsort($myArray);
$ranks = array_fill(1,count($myArray),'foo');
$ranked = array_combine(array_flip($myArray),array_keys($ranks));
/* Output */
print_r($ranked);
/* Array ( [James] => 1 [David] => 2 [Mary] => 3 [Sarah] => 4 ) */
?>
asort returns bool value, as described here.

PHP Typecasting an Int as a String - doesn't seem to work as expected

I am trying to calculate percentiles for users in a database. In order to do this I have a $data array that I need to sort.
I have a query object that contains User_ID, Total_User_Orders, and Total_Orders. Here's how the code looks so far:
// Loop through the users
foreach($query->result() as $row)
{
(string)$user_id = (string)$row->user_id;
$data[$user_id] = number_format((($row->total_user_orders/$row->total_orders)*100), 5);
}
// Sort the $data array
array_multisort($data);
print_r($data);
What (I believe) that should do is typecast $row->user_id (an int) as a string. Then, the $data[$user_id] index should be set as a string - right...?
When I sort the array using array_multisort it sorts it as though the index was an Integer, rather than a String. This means it loses the index.
The PHP manual for array_multisort() states, "Associative (string) keys will be maintained, but numeric keys will be re-indexed.". I have also tried using array_multisort($data, SORT_STRING), but the same output occurs. However - it does work when I do $data['#'.$user_id], but this doesn't quite feel like the right solution to me!
Can anyone help? Thanks in advance!
I think you're overcomplicating things. With no testing, I'd think indexing the $data-array like this would work:
$data[(string)$row->user_id] = ...
or
$data[''.$user_id] = ...
EDIT:
Otherwise you could build your array multi-dimensional and sort by one of the indices, like this:
foreach($query->result() as $row) {
$data[] = array(
'user_id' => $row->user_id,
'percent' => number_format((($row->total_user_orders/$row->total_orders)*100), 5);
);
}
Or you could index by the percentage and sort by the keys (using ksort()):
foreach($query->result() as $row) {
$data[number_format((($row->total_user_orders/$row->total_orders)*100), 5)] = $row->user_id];
}
The last solution could be dangerous if several users have the same percentage.
Personally I would probably go with the asort() solution mentioned above.
As described in my comment, array_multisort() is not what you are after here. You don't have multiple arrays or a multi-dimensional array.
To maintain the key => value association in the array and sort the contents use asort().
foreach ($query->result() as $row) {
$percent = ($row->total_user_orders / $row->total_orders) * 100;
$data[$row->user_id] = number_format($percent, 5);
}
asort($data);
If you want descending percentages reverse the array after it's been sorted.
$data = array_reverse($data, true);

how to compare two arrays and find the count of match elements

i have two arrays i.e$ar1=array("Mobile","shop","software","hardware");and$arr2=arry("shop","Mobile","shop","software","shop")
i want to compare the elements of arr2 to arr1 i.e
foreach($arr2 as $val)
{
if(in_array($val, $arr1))
{
//mycode to insert data into mysql table
variable++; // here there should be a variable that must be increamented when ever match found in $arr2 so that it can be saved into another table.
}
$query="update table set shop='$variable',Mobile='$variable'.......";
}
the $variable should be an integer value so that for later use i can use this variable(shop i.e in this example its value should be 3) to find the match.
My question is how can i get the variable that will increamented each time match found.
Sorry, I don't fully understand the purpose of your code. You can use array_intersect to get common values and array_diff to get the unique values when comparing two arrays.
i want to compare the elements of arr2 to arr1 i.e
Then you are essentially doing the same search for shop three times. It's inefficient. Why not sort and eliminate duplicates first?
Other issues. You are comparing arr2 values with the ones in arr1, which means the number of repetation for "shop" will not be 3 it will be one. Doing the opposite might give you the number of repetation of arr1[1] in arr2 =3.
There are multitude of ways to solve this problem. If efficiency is required,you might wish to sort so you don't have to go beyond a certain point (say s). You can learn to use indexes. Infact the whole datastructure is revolved around these kinds of things - from quick and dirty to efficient.
Not sure I understand the connection between your two arrays. But you can use this to count how many items are in your second array:
$items = array("shop","Mobile","shop","software","shop");
$count = array();
foreach($items as $item)
{
if(isset($count[$item]))
{
$count[$item]++;
}
else
{
$count[$item] = 1;
}
}
print_r($count); // Array ( [shop] => 3 [Mobile] => 1 [software] => 1 )

Categories