$array[$key] = $something creates a new array - php

I have this php code that's supposed to check how many times a single item is in an array and put the amount in the value of that key.
im asking, why does the $duplicates[$item] += 1; create a new array instead of appending it to the existing array
here is the picture of the output I get.
this is my code:
$itemQuery = $con->prepare("SELECT cart_value FROM active_carts WHERE username=:prodName");
$itemQuery->bindParam(":prodName" , $uname , PDO::PARAM_STR);
$itemQuery->execute();
$itemCount = $itemQuery->fetchAll();
$arrax = $itemCount[0]["cart_value"];
$itemArrX = explode(",", $arrax);
$inQuestion = array();
$duplicates = array();
foreach ($itemArrX as $item) {
if (in_array($item , $inQuestion)) {
$counter = 0;
if (!array_key_exists($item , $duplicates)) {
$duplicates[$item] = $counter;
// doesnt even execute
} else {
echo $duplicates[$item]; // echoes a new array every time
$duplicates[$item] += 1;
}
} else {
array_push($inQuestion, $item);
}
}

It is rather simple, you never created that item in the array.
$data[$key] = $value; //examine that.
try this $duplicates[$item] = $item;
then $duplicates[$item] += 1;

If you're just looking for a count of duplicate values, I like the suggestion about array_count_values() in the comments. That can be run through array_filter() to remove the non-duplicate values.
$itemQuery = $con->prepare("SELECT cart_value FROM active_carts WHERE username=:prodName");
$itemQuery->execute([":prodName"=>$uname]);
$itemArrX = explode(",", $itemQuery->fetchColumn(0));
$itemArrX = ["foo","bar","baz","bar","boo","baz"];
$duplicates = array_filter(array_count_values($itemArrX), function($v){return $v>1;});
$inQuestion = array_unique($itemArrX);
print_r($duplicates);
print_r($inQuestion);
Or here's a condensed version of your code which should work just fine.
$itemQuery = $con->prepare("SELECT cart_value FROM active_carts WHERE username=:prodName");
$itemQuery->execute([":prodName"=>$uname]);
$itemArrX = explode(",", $itemQuery->fetchColumn(0));
$itemArrX = ["foo","bar","baz","bar","boo","baz"];
$duplicates = [];
$inQuestion = [];
foreach ($itemArrX as $item) {
if (in_array($item, $inQuestion)) {
echo $duplicates[$item];
$duplicates[$item]++;
} else {
$duplicates[$item] = 1;
$inQuestion[] = $item;
}
}
print_r($duplicates);
print_r($inQuestion);

Related

php foreach loop giving only 1 result from the array

I have a csv file containing attributes of the stores (id, name, category, featured, etc) to be displayed in a project. For now I need to display an array of featured stores with the condition 'featured'='TRUE'. There are 10 results.
Here's the code to read the file and save the data as an associative array
function read_all_stores() {
$file_name = 'csv_files/stores.csv';
$fp = fopen($file_name, 'r');
$first = fgetcsv($fp); // get the first row aka headlines of the file
$stores = [];
while ($row = fgetcsv($fp)) {
$i = 0;
$store = [];
foreach ($first as $col_name) {
$store[$col_name] = $row[$i];
$i++;
}
$stores[] = $store;
}
return $stores;
}
sample result of the first 5 stores
Now I want to display only the stores that has attribute featured = 'TRUE'. I tried this code:
function get_store() {
$stores = read_all_stores();
$feature = [];
foreach ($stores as $s) {
while ($s['featured'] == 'TRUE') {
$feature[] = $s;
return $feature;
}
}
return false;
}
But it only returns one result.
I tried removing the single quotation mark but it seems to only accept the 'TRUE' value as string instead of boolean. How can I fix this foreach loop??
Your problem is that as soon as you find a matching result: $s['featured'] == 'TRUE', you return it: return $feature;. Instead, you need to process all values in $stores before returning your result. If there are matching stores (count($feature) is non-zero i.e. truthy), return them, otherwise return false.
function get_store() {
$stores = read_all_stores();
$feature = [];
foreach ($stores as $s) {
if ($s['featured'] == 'TRUE') {
$feature[] = $s;
}
}
return count($feature) ? $feature : false;
}
Two problems in your code:
In get_store() method you are returning as soon as you find a match. Instead you should add all the matched ones and then return at the end.
For checking a match you should use if instead of while
Here is the modified version of your code:
<?php
function read_all_stores() {
$file_name = 'stores.csv';
$fp = fopen($file_name, 'r');
$first = fgetcsv($fp); // get the first row aka headlines of the file
$stores = [];
while ($row = fgetcsv($fp)) {
$i = 0;
$store = [];
foreach ($first as $col_name) {
$store[$col_name] = $row[$i];
$i++;
}
$stores[] = $store;
}
return $stores;
}
function get_store() {
$stores = read_all_stores();
$feature = [];
foreach ($stores as $s) {
if ($s['featured'] == 'TRUE') {
$feature[] = $s;
}
}
return $feature;
}
echo count(get_store());

PHP Get duplicate values in array and average second value

I have an array that looks like this:
$ratingsInPosts = array
(
array("1",3),
array("2",5),
array("2",2),
array("5",2),
array("90",1),
array("5",6),
array("2",2),
);
I Want to find duplicate values in the first column and avarage its values from the second column.
So that this("1",3),("2",5),("2",2),("5",2),("90",1),("5",6),("2",2)
ends up like this ("1",3),("2",3),("5",4),("90",1)
Try this tested solution
I got the required Output
$ratingsInPosts = array
(
array("1",3),
array("2",5),
array("2",2),
array("5",2),
array("90",1),
array("5",6),
array("2",2),
);
$arr1 = array_column($ratingsInPosts, 0);
$p = array_count_values($arr1);
foreach($p as $key => $value)
{
$sum = 0;
for($i=0; $i < $value; $i++)
{
$pos = array_search($key, $arr1);
$sum += $ratingsInPosts[$pos][1];
unset($arr1[$pos]);
unset($ratingsInPosts[$pos]);
}
$re[] = array('"'.$key.'"',$sum/$value);
}
print_r($re);
I hope it helps you:
$groups = array();
// in this loop we group values by first column
foreach ($ratingsInPosts as $row) {
$key = $row[0];
if (!isset($groups[$key]) {
$groups[$key] = array();
}
$groups[$key][] = $row[1];
}
$result = array();
foreach ($groups as $key => $value) {
$avg = array_sum($value) / count($value);
$row = array($key, $avg);
$result[] = $row;
}
<?php
header('Content-Type: text/plain');
$ratingsInPosts = array(array("1",3),array("2",5),array("2",2),array("5",2),array("90",1),array("5",6),array("2",2));
$result = array();
$output = array();
foreach($ratingsInPosts as $array){
$result[$array[0]][] = $array[1];
}
foreach($result as $key=>$array){
$output[] = array($key,round(array_sum($array)/count($array)));
}
var_export($output);
?>

getting duplicated results from nested foreach loop

I'm trying to get the following output from mysql for Google Line Chart API:
[["product","diameter","width"],["Product 1","2","4"],["Product 2","4","8"]]
I have set up several input checkboxes to send field names (e.g width,diameter) to the database via $_POST["info"] and retrieve the values from those fields. Here's the part that generates the data from mysql:
$result = $users->fetchAll();
$comma = "";
$data="";
$data[0] = array_merge(array(product),$info);
$i = 1;
foreach ($result as $r)
{
foreach($_POST["info"] as $p)
{
$d .= $comma.$r[$p]; // trying to get "$r["width"],$r["diameter"]"
}
$comma = ",";
$data[$i] = array($r["name"], $d);
$i++;
}
echo json_encode($data);
My desired output should be like this:
[["product","diameter","width"],["Product 1","2","4"],["Product 2","4","8"]]
But that code is generating duplicated results like this
[["product","diameter","width"],["Product 1","24"],["Product 2","24,4,8"]]
I guess I shouldn't be using the nested foreach to loop over $_POST. Can anyone tell me how to fix that?
Full PHP Code:
$info = $_POST["info"]; // It contains an array with values like width,diameter,thickness etc...
$comma = "";
foreach($info as $in)
{
$field .= "".$comma."b.".$in."";
$comma = ",";
}
$sql = "
SELECT {$field},a.user_id,a.name
FROM `product_detail` a INNER JOIN
`attr` b ON a.model = b.model
WHERE a.user_id = ?
GROUP BY a.model
";
$users = $dbh->prepare($sql);
$users->bindValue(1, $_SESSION["user_id"]);
$users->execute();
$result = $users->fetchAll();
$comma = "";
$data="";
$i = 1;
$data[0] = array_merge(array(product),$info);
foreach ($result as $r)
{
foreach($_POST["info"] as $p)
{
$d .= $comma.$r[$p];
}
$comma = ",";
$data[$i] = array($r["name"], $d);
$i++;
}
echo json_encode($data);
$_POST["info"] Content:
Array
(
[0] => diameter
[1] => width
)
try it like this:
$result = $users->fetchAll();
$data="";
$data[0] = array_merge(array(product),$info);
$i = 1;
foreach ($result as $r)
{
$d[]=$r["name"];
foreach($_POST["info"] as $p)
{
$d[]= $r[$p];
}
$data[$i] = $d;
$d=array(); //set $d to empty not to get duplicate results
$i++;
}
echo json_encode($data);
The end result you are looking for, is valid JSON. You should not try to manually generate that.
Instead you should make an array of arrays in php and use json_encode($array) to get the result you are looking for.
Also note that by injecting your POST variables directly in your query, you are vulnerable to sql injection. When accepting fields, you should check them against a white-list of allowed values.
Try the below solution:
$result = $users->fetchAll();
$data="";
$data[0] = array_merge(array(product),$info);
$i = 1;
foreach ($result as $r)
{
$d = array();
foreach($_POST["info"] as $p)
{
$d[] = $r[$p]; // trying to get "$r["width"],$r["diameter"]"
}
$data[$i] = array($r["name"]) +$d;
$i++;
}
echo json_encode($data);

How to get last value of for loop?

I'm trying to get the value of $row->price for the last iteration in my for loop as below
foreach ($getprices->result() as $row)
{
if ($bb=='on'){
$pric = $row->price+2.50;
$pri = number_format($pric,2);
}else{
$pric = $row->price;
$pri = number_format($pric,2);
}
I did try the following, however it didn't appear to work
$numItems = count($getprices->result());
$i = 0;
foreach($getprices->result() as $row) {
if(++$i === $numItems) {
if ($bb=='on'){
$pric = $row->price+2.50;
$pri = number_format($pric,2);
}else{
$pric = $row->price;
$pri = number_format($pric,2);
}
}
}
Any suggestions?
Use $row->price just after the loop has ended:
foreach ($getprices->result() as $row)
{
// ...
}
$lastprice = $row->price;
This is really just a trick, but it will work. If you are iterating over an array you can also do it like this:
$array = $getprices->result();
foreach ($array as $row)
{
// ...
}
$lastprice = end($array)->price; // this will work independently of any loop
Add a line $lastitem=$row right after the for statement ; it will contain the last value after the loop is finished
You can use php end to get the last value of an array.
eg;
$arr = $getprices->result();
$last = end($arr);

Remove empty array elements from the $_FILES array

I have a photo album, with a 'photos' field in database which has serialized data and is base 64 encoded. I can upload up to 21 photos at the same time for the album (multiple uploading). When trying to add new photos to the album on edit mode, I can't get the new $_FILES array to merge with the old array in the database. I want to update only the values that I have changed. So let's say I already had 2 images inside my album, I would like to add a 3rd image without losing the other 2 images. I know I need to use unset() to delete empty values but I think i'm not doing it correctly. Here's my code:
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'edit') {
//select photo album and get photos and descriptions to be merged with new.
$album_id = $_REQUEST['album_id'];
$old_photos = null;
$old_descriptions = null;
$getAlbumQ = mysql_query("select * from albums where id='$album_id'");
while ($old_album = mysql_fetch_array($getAlbumQ)) {
$old_photos = unserialize(base64_decode($old_album['photos']));
$old_descriptions = unserialize(base64_decode($old_album['descriptions']));
}
if (isset($_POST['album_name']) && isset($_POST['desc'])) {
$name = $_POST['album_name'];
$desc = $_POST['desc'];
$idesc = array();
$target_path = "../uploads/albums/";
foreach ($_FILES as $k => $v) {
//first upload photos
$path = $target_path . basename($v['name']);
if(move_uploaded_file($v['tmp_name'], $path)) {
$hasUpload = true;
}
}
for ($j = 1; $j < 21; $j++) {
$img_index = $j;
$img_desc = $_POST['desc_' . $img_index];
$asdf = $_FILES['image_' . $img_index]['name'];
array_push($idesc, $img_desc);
}
foreach( $_FILES['images']['name'] as $key => $value ) {
if( empty($value) ) {
unset( $_FILES['images']['name'][$key] );
}
}
for ($i = 1; $i < 21; $i++) {
if($_FILES['image_'.$i]['name']!= '')
{
$hasUpload = true;
$presults = array_merge($old_photos, $_FILES); //THE PROBLEM WITH MERGING ARRAYS OCCURS HERE
$dresults = array_merge($old_descriptions, $idesc);
$images = base64_encode(serialize($presults));
$descriptions = base64_encode(serialize($dresults));
$posted = date("Y-m-d H:i:s");
}
else {
$hasUpload = false;
$presults = $old_photos;
$dresults = $idesc;
$images = base64_encode(serialize($presults));
$descriptions = base64_encode(serialize($dresults));
$posted = date("Y-m-d H:i:s");
}
}
}
}
I tried something similar to what Muu suggested. Combined it with another piece of code of a custom merge function found here http://keithdevens.com/weblog/archive/2003/Mar/30/MergeTwoArrays and it worked fine! Thanks for your help
Just needed to add the following lines of code outside the for ($i = 1; $i < 21; $i++) loop:
//delete empty values for $_FILES array
foreach ($_FILES as $key => $value) {
foreach ($value as $k => $v) {
if ($k=='name' && $v!='') {
break;
} else {
unset($_FILES[$key]);
}
}
}
//custom array merge function
function merge(&$a, &$b){
$keys = array_keys($a);
foreach($keys as $key){
if(isset($b[$key])){
if(is_array($a[$key]) and is_array($b[$key])){
merge($a[$key],$b[$key]);
}else{
$a[$key] = $b[$key];
}
}
}
$keys = array_keys($b);
foreach($keys as $key){
if(!isset($a[$key])){
$a[$key] = $b[$key];
}
}
}
then instead of array_merge(), I used merge() inside the for ($i = 1; $i < 21; $i++) loop:
$presults = merge($old_photos, $_FILES);
Instead of removing the empty values from the array, why not just ignore them?
For example:
$nonEmptyArray = array();
foreach ($_FILES as $k => $v) {
if (!$v) {
continue;
}
$nonEmptyArray[$k] = $v;
}
$_FILES = $nonEmptyArray;

Categories