How to find the keys of duplicate entries in multidimensional array - php

I'm reporting on appointment activity and have included a function to export the raw data behind the KPIs. This raw data is stored as a CSV and I need to check for potentially duplicate consultations that have been entered.
Each row of data is assigned a unique visit ID based on the patients ID and the appointment ID. The raw data contains 30 columns of data, the duplicate check only needs to be performed on 7 of these. I have imported the CSV and created an array as below for first record and then append rest on.
$mds = array(
$unique_visit_id => array(
$appt_date,
$dob,
$site,
$CCG,
$GP,
$appt_type,
$treatment_scheme
)
);
What I need is to scan the $mds array and return an array containing just the $unique_visit_id for any duplicate arrays.
e.g. keys 1111, 2222 and 5555 all references arrays that contain the same value for all seven values, then I would need 2222 and 5555 returned.
I've tried search but not coming up with anything that is working.
Thanks

This is what I've gone with, still validating (data set is very big) but seems to be functioning as expected so far
$handle = fopen("../reports/mds_full_export.csv", "r");
$visits = array();
while($data = fgetcsv($handle,0,',','"') !== FALSE){
$key = $data['unique_visit_id'];
$value = $data['$appt_date'].$data['$dob'].$data['$site'].$data['$CCG'].$data['$GP'].$data['$appt_type'].$data['$treatment_scheme'];
$visits[$key] = $value;
}
$visits = asort($visits);
$previous = "";
$dupes = array();
foreach($visits as $id => $visit){
if(strcmp($previous, $visit) == 0){
$dupes[] = $id;
}
$previous = $visit;
}
return $dupes;

Related

Getting a an array from for loop and use another for loop with that array in PHP

I have a for loop, and will form two arrays in the loo
foreach ($data as $key => $value) {
........
........
$user_insert[] = [
'keyy' => $value,
'key' => $value,
....
...
...
];
$someArray1[] = [
/*'user_id' => $insert_id,*/
'key1' => $value1,
'keyy' => $value,
.......
........
];
}
the count of $user_insert[] array is 4, the count of $someArray1 is 15.
after this for loop, I need to insert $user_insert array data to the database and use that inserted_id to insert next array $someArray1
foreach($user_insert as $insert_user){
$unique_user_insert = array_unique($insert_user);
//dd($unique_user_insert);
$insert_id = DB::table('users')->insertGetId($unique_user_insert);
foreach ($someArray1 as $someArray) {
$someArray['user_id'] = $insert_id;
DB::table('table_name')->insert($someArray);
}
}
So the problem here is the data in the second loop is inserting 60 times(4 * 15). I need to insert only 15 rows.
The data($someArray1) is coming from the first for loop, but I need to add a user_id to that array which I get after the insert operation in second for loop.
So how can i insert only 15 rows.
I'm going to assume that you are able to access your $someArray1 using the $insert_id value to find the appropriate user data.
foreach($user_insert as $insert_user){
$unique_user_insert = array_unique($insert_user);
$insert_id = DB::table('users')->insertGetId($unique_user_insert);
// Get the user information you need as $someArray1 should be user_id=>data
$userData = $someArray[$insert_id];
$userData['user_id'] = $insert_id;
// No need for an inner loop, just access the necessary properties of the loop you created earlier.
DB::table('table_name')->insert($userData);
}
Your tags indicate that you are using Laravel 5 too. If you are using the eloquent ORM, some of the insertion and ID retrieval can be cleaned up by creating Models for your DB tables.
Actually, each time you process a line from $user_insert, you loop over $someArray1 instead of fetching just the line you need.
The thing is to understand the line you need. As much as I can understand your piece of code, I would say the easiest (most readable) way of doing it is by using a for loop, not a foreach one :
for( $i = 0, $iMax = count( $user_insert ); $i < $iMax; ++$i ){
$insert_user = $user_insert[$i];
// Put your `$user_insert` insert code here
$someArray1[$i]['user_id'] = $insert_id;
DB::table('table_name')->insert( $someArray[$i] ); // Note the [$i] here
}
You also may do that with foreach by requesting indices :
foreach( $user_insert as $i => $insert_user ){
$unique_user_insert = array_unique($insert_user);
//dd($unique_user_insert);
$insert_id = DB::table('users')->insertGetId($unique_user_insert);
// Now use $i requested above :
$someArray1[$i]['user_id'] = $insert_id;
DB::table( 'table_name' )->insert( $someArray1[$i] );
}

How to sort a multidimensional array of columns by a specific column in PHP?

I found on the PHP documentation the function "array_multisort" that is meant to sort an array "of columns". They provide one example where the user has an array of rows and then the user has to transform this array of rows into an array of columns. In my case the array is already set by columns, such as:
tablearray
{
['Employee_ID'] = {0 => row1, 1 => row2, 2 => row3, 3 => row4}
['First_Name'] = {0 => row1, 1 => row2, 2 => row3, 3 => row4}
['LastName'] = {0 => row1, 1 => row2, 2 => row3, 3 =>row4}
}
I want to sort by Employee_ID and I need all the other columns to follow the same order. I tried:
array_multisort($tablearray['Employee_ID'], SORT_ASC);
But it only sorts the first column (which becomes a mess). The array has more than 10 columns and it changes the column names depending on the search (the columns names are its keys).
On PHP's documentation for this function, the example provided shows that the after transforming the rows array into a columns array, we should use the original array as a third parameter to match the keys - I don't have the "original" array to do the match since I didn't transform anything.
Thank you.
Desired output, as suggested by one user:
Original:
array
{
['Employee_ID'] = (1002, 4508, 0002, 1112)
['Business_Unit'] = ('UER', 'ABC', 'XYZ', 'EER')
['LastName'] = ('Smith', 'Vicente', 'Simpson', 'Thompson')
}
Sorted by Employee ID:
array
{
['Employee_ID'] = (0002, 1002, 1112, 4508)
['Business_Unit'] = ('XYZ', 'UER', 'EER', 'ABC')
['LastName'] = ('Simpson','Smith', 'Thompson', 'Vicente')
}
--
My original array is a database query output:
Array
(
[0] => Array
(
[Employee_ID] => 0000
[Supervisor_ID] => 00000
[Location_Descr] => somewhere
[Start_Date] => 06/03/2002
[Service_Date] => 06/03/2002
[Rehire_Date] => 00/00/00
[Business_Unit] => YYYY
[Job_Title] => Manager
[Email] => email#example.com
[Dept_Desc] => bla bla bla
[Employee_Name_LF] => Last, First
[Supervisor_Name_LF] => Last, First
[Term_Date] => 00/00/00
[Preferred_Name] => Someone
[Source] => Sheet2
)
)
There a several more rows.
The main purpose is to show the results as an HTML table and to generate a CSV file. I already made those functions using the modified structure (the first that I posted). I thought it would be easier to deal with that structure... Indeed it was, but not for sorting unfortunately.
The array_multisort documentation (http://php.net/manual/en/function.array-multisort.php) suggests separating each column as an individual array.. However, as you can see I have several columns (and the user can select more or less to be shown before performing the query.. So I can't just list all of them on the statement).
I a willing to change everything just to make the code better to be worked with.
Ugly - would be a lot easier if you formatted the input tables.
$arr = array(
'Employee_ID' => array('1002', '4508', '0002', '1112'),
'Business_Unit' => array('UER', 'ABC', 'XYZ', 'EER'),
'LastName' => array('Smith', 'Vicente', 'Simpson', 'Thompson')
);
$employees = array();
foreach (range(0, sizeof($arr[current(array_keys($arr))]) - 1) as $k) {
$emp = array();
foreach ($arr as $col => $vals) {
$emp[$col] = $arr[$col][$k];
}
$employees[] = $emp;
}
$sort = array();
foreach ($employees as $k => $v) {
$sort[$k] = $v['Employee_ID'];
}
array_multisort($sort, SORT_ASC, $employees);
print_r($employees);
And to put back in the original format:
$arr_sorted = array();
foreach (array_keys($arr) as $col) {
$arr_sorted[$col] = array();
foreach ($employees as $emp) {
$arr_sorted[$col][] = $emp[$col];
}
}
print_r($arr_sorted);
Thank you for posting the extra details in your question, as they did help in understanding the intent of your question.Now, you didn't tell us how that table should look; If you want the employees one per row, or one per column. Which is kind of crucial to know. Normally one would have one employee per line, especially if this is to be exported to CVS. However, I have a suspicion that it's the latter you want. Otherwise you've gone about this in a very overly complicated manner.Point in case: Normal one-per-row layout:
<?php
$db = new PDO();
// Defining the fields we need here, to avoid having too long a string for the query.
$fields = "e.employee_id, e.first_name, e.lastname, u.business_unit, s.email";
// Do the sorting in the database itself. Not only is this faster, but it
// is also a lot easier to sort it exactly as you'd like.
// Note that I don't use prepared statements here, as there is no user-input.
$query = <<<outSQL
SELECT {$Fields} FROM `unit` AS u
INNER JOIN `employee` AS e ON e.employee_id = u.unit_id
INNER JOIN `employee` AS s ON s.employee_id = u.supervisor_id
ORDER BY e.`employee_id`
outSQL;
$data = $db->query($query);
// Creating a printf() template for the output, to make the code easier to maintain.
$rowTemplate = <<<outHTML
<tr>
<td>%1\$d</td>
<td>%2\$s</td>
<td>%3\$s</td>
</tr>
outHTML;
// Generate the table template, using placeholders for where the data will be added..
$tableTemplate = <<<outHTML
<table>
<thead>
<tr>
<th>ID</th>
<th>First name</th>
<th>Last name</th>
</tr>
</thead>
<tbody>
%s
</tbody>
</table>
outHTML;
// Regular table output, one employee per line.
$temp = '';
foreach ($data as $e) {
// hs() is a shortcut function to htmlspecialchars (), to prevent against XSS.
$temp .= sprintf($rowTemplate, $e['employee_id'], hs($e['first_name']), hs($e['lastname']));
}
// Add the rows to the table, so that you can echo the completed product wherever you need.
$employeeTable = sprintf($tableTemplate, $temp);
If you want to do it one per column, it becomes a bit more intricate. Though, still a bit easier than your first attempt. :)
Namely, something like this:
<?php
$db = new PDO();
// Defining the fields we need here, to avoid having too long a string for the query.
$fields = "employee_id, first_name, lastname";
// Do the sorting in the database itself. Not only is this faster, but it
// is also a lot easier to sort it exactly as you'd like.
// Note that I don't use prepared statements here, as there is no user-input.
$data = $db->query("SELECT {$Fields} FROM `employees` ORDER BY `employee_id`");
// We need to know how many columns we'll have. One per employee.
$columns = count ($data);
// Rows have a header in front of each line, and one td tag for each employee.
$rowTemplate = "\t\t<th>%s</th>\n".str_repeat("\t\t\t<td>%s</td>\n", $columns);
// Generate the table template, using placeholders for where the data will be added..
$tableTemplate = <<<outHTML
<table>
<tbody>
%s
</tbody>
</table>
outHTML;
// Reformat the array to give us the data per-column.
$temp = array ();
foreach ($data as $field => $e) {
// Since we've already sorted the data in the database we don't need to do any further sorting here.
// Also note that I'm doing the escaping here, seeing as this array will only be used for output.
$temp['Employee ID'][] = intval($e['employee_id']);
$temp['First name'][] = hs($e['first_name']);
$temp['Last name'][] = hs($e['lastname']);
}
// Now we do the same as in the above example.
$rows = '';
foreach ($temp as $label => $l) {
// We have the label as the first template variable to be added, so put it as the first element.
array_unshift($l, $label);
// Add the current row of items to the output, using the previously established template.
$rows = vprintf($rowTemplate, $l);
}
// Add the rows to the table, so that you can echo the completed product wherever you need.
$employeeTable = sprintf($tableTemplate, $temp);
PS: Haven't tested the code, but it should work.
I ran into his problem and after much angst found a really nice solution in the notes on the php manual page - I now have the following function which i use whenever I need to solve this type of problem.
function fnArrayOrderby(){
//function to sort a database type array of rows by the values in one or more column
//source http://php.net/manual/en/function.array-multisort.php - user notes
//example of use -> $sorted = fnArrayOrderby($data, 'volume', SORT_DESC, 'edition', SORT_ASC);
$args = func_get_args(); //Gets an array of the function's argument list (which can vary in length)
//echo "sorting ".$args[0]."<br>";
if (!isset($args[0])) { return;}
$data = array_shift($args); //Shift an element off the beginning of array
foreach ($args as $n => $field) {
if (is_string($field)) {
$tmp = array();
foreach ($data as $key => $row)
$tmp[$key] = $row[$field];
$args[$n] = $tmp;
}
}
$args[] = &$data;
call_user_func_array('array_multisort', $args);
return array_pop($args);
}

How can I loop through an array while averaging the values of one element and only keep the newly averaged field in PHP?

I have database that contains scores which are stored daily. I want to average each months scores for each user. So far I have this:
DB structure:
id | name | tscore | added
int| string | float(100 or less)| date(2014-01-01 16:34:22)
Code:
while($row = mysql_fetch_assoc($getChartData)){ // Data from MySQL
$added_date = explode(' ',$row['added']); // Date formate 2014-01-01 16:34:22
$chartData[] = array(
'id' => $row['name'],
'tscore' => $row['tscore'],
'added' => $added_date[0] // Here I take the month only
);
}
if($_POST['range'] == 'month'){
foreach($chartData as $key => $value){
$added = explode('-',$chartData[$key]['added']);
$count = 1;
foreach($chartData as $key2 => $value2){
$added2 = explode('-',$chartData[$key2]['added']);
if($chartData[$key]['id'] === $chartData[$key2]['id'] && $added[1] === $added2[1]){ // if user is the same and the month is the same, add the scores together, increment counter, and unset 2nd instance
$chartData[$key]['tscore'] = ((float)$chartData[$key]['tscore'] + (float)$chartData[$key2]['tscore']);
$count++;
unset($chartData[$key2]);
}
}
$chartData[$key]['tscore'] = ($chartData[$key]['tscore']/$count); // Average all the scores for the month.
}
}
The problem is this method is deleting all the elements of the $chartData array. What can I try to resolve this?
You should try to solve it with MySQL. Try something like this (replace 'your_scores_table' with your table name):
SELECT
Score.name,
AVG(Score.tscore) AS `avg`,
CONCAT(YEAR(Score.added), '-', MONTH(Score.added)) AS `year_month`
FROM
your_scores_table AS Score
GROUP BY
Score.name ASC,
YEAR(Score.added) DESC,
MONTH(Score.added) DESC
;
Your logic is wrong. You are looping through the same array twice. Which means that the following if will always evaluate to true which means that array item will always get unset
//This will always be true
if($chartData[$key]['id'] === $chartData[$key2]['id'] && $added[1] === $added2[1]){
It may be simpler for you to create another array where you keep your scores. Something like
$aScores = array();
$count = 1;
foreach($chartData as $key => $value){
//Add score to a different array
$aScores[$value['name']]['tscore'] = (($aScores[$value['name']]['tscore'] + $value['tscore']) / $count);
$count++;
}
Also I would look into the MySQL AVG function. You could use that to save you having to do it in PHP

Create multidimensional array from database query

I have a database that stores accounts and subscriptions. An account can contain accounts and subscriptions and have more than three parent levels.
A row from the database query result looks something like this:
$array = (0 => array("level" => "2", "accountno" => "123124234", "accountname" => "Hansel", "parentaccountno" => "000213123", "subscription" => "5423213213", "username" => "Gretchen");
(Level 1 means that it is top level and it will have PARENTACCOUNTNO = null).
From this I am trying to create a multidimensional array which looks something like this:
accounts:
000213123
accounts:
123124234
name: Hansel
subscriptions:
5423213213
username: Gretchen
This is the code I have so far, which works really well on the first two levels. When there are are three levels or more things become more complicated, since each account only knows about its own parent:
$hierarchy = array();
foreach ($decode as $row) {
$accountno = $row["ACCOUNTNO"];
$msisdn = $row["MSISDN"];
if ($row["PARENTACCOUNTNO"] == null)
$base_array_key = $hierarchy["accounts"];
$base_array_key[$accountno] = array("name" => $row["ACCOUNTNAME"], "accounts" => array(), "subscriptions" => array());
$hierarchy["accounts"][$accountno]["accounts"]["321323123213"] = "blaha";
if ($row["MSISDN"] != null)
$hierarchy["accounts"][$accountno]["subscriptions"][$msisdn] = array("enduser" => $row["ENDUSER"]);
}
The solution I have in mind right now is to pass the base key from each iteration to the next iteration, and then if for example the previous level was two levels above this one just use some sort of string replace to remove the last two parts of the key.
Any better ideas? I am quite sure this is a shitty way of doing what I am trying to accomplish.
Do all your results look like that ? So then, where's are the parent accounts ? Or am i missing something ? I would simple rewrite the query to get a more "linear" result, like:
parentaccountno | accountno | accountname | bla
-----------------------------------------------
000213123 | 123124234 | Hansel | abc
When every result row looks like this, you can simple create your final array like that:
foreach ($decode as $row) {
$my_array = [ $row["parentaccountno"] ][ $row["accountno"] ]["accountname"] = $row["accountname"];
$my_array = [ $row["parentaccountno"] ][ $row["accountno"] ]["bla"] = $row["bla"];
}
Create a temporary associative array:
$all_records = array();
Populate it with the results you find in the database:
while($result = get_results_from_db()) {
$accountno = $result[accountno];
$all_records[$accountno] = $result;
}
Now you have an array indexed by account numbers so it's easy to find any record if you know it's account number. So you iterate through the array to associate children to parents:
$top_level_records = array();
foreach($all_records AS $record) {
$parentaccountno = $record[parentaccountno];
if(!empty($parentaccountno)) {
$parent = &$all_records[$record[parentaccountno]];
if(empty($parent['accounts']))
$parent['accounts'] = array()
$parent['accounts'][] = $record;
}
else {
$top_level_records[] = $record;
}
}
I appreciate the answers, but I think the problem is my database query which should contain records of all the parents of an account and not just the parent, using something like SYS_CONNECT_BY_PATH in Oracle SQL.

adding elements in an array day-wise from mysql database

How can I get the record values in the database to be sorted like this in an array. Supose I am adding the day no.
array
[0] => array=>'id'=>'26' 'date'=>'26'
[1] => array=>'id'=>'27' 'date'=>'27',
array=>'id'=>'28' 'date'=>'27',
array=>'id'=>'29' 'date'=>'27'
[2] => array=>'id'=>'30' 'date'=>'29'
[3] => array=>'id'=>'31' 'date'=>'31',
array=>'id'=>'32' 'date'=>'31',
array=>'id'=>'33' 'date'=>'31'
Basically, I want to add an array to the same index if the next id contains a record with the same date (day no) of the month. Otherwise add it normally.
Right now, My function is adding the rows of the record without sorting it in the format I want it to be in.
The reason I want it to be in this format is because, I need to run the foreach, and if 1 day contains 2 records, then it will append another <li> into my unordered list.
public function getArticles()
{
$sql = 'CALL getArticles()';
$articles = Array();
if (Model::getConnection()->multi_query($sql)) {
do {
if ($result = Model::getConnection()->store_result()) {
while ($row = $result->fetch_assoc()) {
array_push($articles,$row);
}
$result->free();
}
} while (Model::getConnection()->next_result());
}
return $articles;
}
I don't recognize what some of your code is doing, but I think this is the important part.
while ($row = $result->fetch_assoc()) {
if (!isset($articles[$row['date']])) {
$articles[$row['date']] = array();
}
$articles[$row['date']][] = $row;
}
The only difference will be that your array will be keyed on the date instead of incrementing from zero. If you really want it reindexed, you can do...
array_values($articles);
As savinger pointed out, there is alot of functionality in your code which I don't really know, so I've included comments to indicate whereabouts the process is:
// Start of your loop
// $value is what you're getting from the DB...
$array_search = array_search($value,$main_array);
if($array_search)
{
// If this value already exists, we need to add
// this value in +1 of its current value
$main_array[$array_search][] = $value + 1;
}
else
{
// Make a new array key and add in the value
$main_array[] = $value;
}
// End of your loop

Categories