I am developing a user driven eCommerce website and need some help. What I have is a function that will loop through an array remove duplicates and how many times they occur. I then need to run a function on each of those extracted duplicates as many times as they occur. The code I have so far works, but breaks when there are multiple duplicates with the same repetition count. Here is the code I have made so far..
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14); //breaks the code
$affiliates = array(11,11,13,13,13,14,14,14,14,12,12,12,12,12); // works fine
$array = array();
$match_count = array();
foreach($affiliates as $key => $affiliate) {
$array[] = $affiliate;
}
arsort($array); // keeps array index in order
foreach($array as $arrays) {
if(array_value_count($arrays,$array) > 1) {
$match_count[] = array_value_count($arrays,$array);
}
}
$match_count = array_unique($match_count);
$array_unique = arrayDuplicate($array);
$final_array = array_combine($match_count,$array_unique);
foreach($final_array as $key => $value) {
for($i = 0; $i < $key; $i++) {
echo 'addOrder(affiliate_id = ' . $value . ') . '<br>';
}
}
the functions
function unique_array($array) {
return array_unique($array, SORT_NUMERIC);
}
function arrayDuplicate($array) {
return array_unique(array_diff_assoc($array,array_unique($array)));
}
function array_value_count($match, $array) {
$count = 0;
foreach ($array as $key => $value)
{
if ($value == $match)
{
$count++;
}
}
return $count;
}
to fix the duplicates breaking the code I have tried this
if(count($array_unique) - count($match_count_unique) == 1 ) // do something
or
if(count($array_unique) != count($match_count_unique) == 1 ) // do something
How would I know where to add the missing duplicate value count and array items correctly without them getting out of sync? OR Is there a better way of doing this?
Taken from How do I count occurrence of duplicate items in array
$array = array(12,43,66,21,56,43,43,78,78,100,43,43,43,21);
$vals = array_count_values($array);
echo 'No. of NON Duplicate Items: '.count($vals).'<br><br>';
print_r($vals);
Result
No. of NON Duplicate Items: 7
Array
(
[12] => 1
[43] => 6
[66] => 1
[21] => 2
[56] => 1
[78] => 2
[100] => 1
)
Duplicate items = (Array Size) - (Total Number of Unique Values)
<?php
$affiliates = array(11,11,12,12,13,13,13,14,14,14,14);
// get an array whose keys are the aff# and
//the values are how many times they occur
$dupes = array();
foreach ($affiliates as $aff) {
$dupes[$aff]++;
}
// remove the 1's since those aren't dupes
$dupes = preg_grep('~^1$~',$dupes,PREG_GREP_INVERT);
// de-dupe the original array
$affiliates = array_unique($affiliates);
// for each duped affiliate...
foreach ($dupes as $aff => $affCount) {
// for each time it was duped..
for ($c=0;$c<$affCount;$c++) {
// do something. $aff is the aff# like 11
}
}
?>
Related
I am trying to rank an array of values and maintain the order in the original array.
So for example:
(6,4,7,12,7) should return (2,1,3,4,3)
(12,17,5,27,5) should return (2,3,1,4,1)
(1,1,4,6) should return (1,1,2,3)
In each case the returned array has the rank of the corresponding element in the original array, and duplicate values will have the same rank.
$values = array(12,17,5,27,5);
$sorted_values = $values;
sort($sorted_values);
foreach ($values as $key => $value) {
foreach ($sorted_values as $sorted_key => $sorted_value) {
if ($value == $sorted_value) {
$rank_key = $sorted_key;
break;
}
}
echo $value . ' has rank: ' . $rank_key + 1 . '<br>';
}
A simple way would be to first rank the unique values (using array_unique() followed by sort()), then translate the original list by this newly rank list (more comments in the code)...
$source = [12,17,5,27,5];
$output = [];
// Copy to work array unique values
$rank = array_unique($source);
// Sort it to produce ranking order
sort($rank);
// Make the number the key with the order as the value
$rank = array_flip($rank);
// Translate the values using $rank
foreach ( $source as $element ) {
$output[] = $rank[$element]+1;
}
print_r($output);
gives...
Array
(
[0] => 2
[1] => 3
[2] => 1
[3] => 4
[4] => 1
)
Clone your array, sort the clone by values, while keeping the key associations.
Loop over the clone, and in classical “control break” fashion, compare the value of the current item to that of the previous one. Increase the rank by one, if they differ. Set that rank as new value under that key.
Sort the clone by keys.
$data = [12,17,5,27,5];
$clone = $data;
asort($clone);
$rank = 0;
$previous_value = null; // something that doesn't ever occur in your array values
foreach($clone as $key => $value) {
if($value !== $previous_value) {
$rank++;
}
$clone[$key] = $rank;
$previous_value = $value;
}
ksort($clone);
var_dump($data, $clone);
you need to sort your array
This may close to your answer:
<?php
$my_array=array(12,17,5,27,5);
$ordered_values = $my_array;
$rank=array();
rsort($ordered_values);
foreach ($ordered_values as $key => $value) {
foreach ($ordered_values as $ordered_key => $ordered_value) {
if ($value === $ordered_value) {
$key = $ordered_key;
break;
}
}
$rank[$value]=((int) $key + 1) ;
}
foreach($my_array as $key => $value)
{
echo $value.':'.$rank[$value].'<br>';
}
?>
function rankDuplicateArray(array $array): array
{
$copy = array_unique($array);
sort($copy);
$flipArray = array_flip($copy);
return array_map(function($v) use ($flipArray) {
return $flipArray[$v] + 1;
}, $array);
}
$feetypes=[
[30,35,50],
[30,35],
[30,50],
[30,34]
];
i have this code, want to count as per there value like count 30*4, 35*2, 50*2 & 34*1.
i have already tried:
$counts = array();
foreach ($array as $key=>$subarr) {
// Add to the current group count if it exists
if (isset($counts[$subarr['group']]) {
$counts[$subarr['group']]++;
}
// or initialize to 1 if it doesn't exist
else $counts[$subarr['group']] = 1;
// Or the ternary one-liner version
// instead of the preceding if/else block
$counts[$subarr['group']] = isset($counts[$subarr['group']]) ? $counts[$subarr['group']]++ : 1;
}
but my problem still there
You can merge all the inner arrays into one with array_merge and then use array_count_values to get the counts.
$counts = array_count_values(array_merge(...$feetypes));
Merge all the subarray into a single array and apply array_count_values function to get the result
function merageAll($arr) {
$flatArray = array();
foreach($arr as $element) {
if (is_array($element)) {
$flatArray = array_merge($flatArray, merageAll($element));
} else {
$flatArray[] = $element;
}
}
return $flatArray;
}
$res = array_count_values(merageAll($feetypes));
Result
Array
(
[30] => 4
[35] => 2
[50] => 2
[34] => 1
)
function merageAll work if there are values and sub arrays in the array.
Working DEMO LINK
I used recursion in case values are more than two levels deep. note the $count variable is passed by reference to the function due to recursion.
<?php
$feetypes=[
[30,35,50],
[30,35],
[30,50],
[30,34]
];
$counts = array();
function countValues($arr, &$count) {
foreach($arr as $subval) {
if(is_array($subval)) {
countValues($subval,$count);
} else {
if(isset($count[$subval])) {
$count[$subval] += 1;
} else {
$count[$subval] = 1;
}
}
}
}
countValues($feetypes,$counts);
print_r($counts);
I have an array which comes from a report.
This report has info similar to:
157479877294,OBSOLETE_ORDER,obelisk,19/01/2013 01:42pm
191532426695,WRONG_PERFORMANCE,g3t1,19/01/2013 01:56pm
159523681637,WRONG_PERFORMANCE,g3t1,19/01/2013 01:57pm
176481653889,WRONG_PERFORMANCE,g4t1,19/01/2013 01:57pm
167479810401,WRONG_PERFORMANCE,g4t1,19/01/2013 02:00pm
172485359309,WRONG_PERFORMANCE,g4t2,19/01/2013 02:02pm
125485358802,WRONG_PERFORMANCE,g4t2,19/01/2013 02:02pm
172485359309,DAY_LIMIT_EXCEEDED,obelisk,19/01/2013 02:03pm
125485358802,DAY_LIMIT_EXCEEDED,obelisk,19/01/2013 02:03pm
What I need to do is get the total of each type of error and the location so for the first would be error: 'OBSOLETE_ORDER' and location: 'obelisk'. I have tried to do this a number of ways but the best I can come up with is a multi dimensional array:
$error_handle = fopen("$reportUrl", "r");
while (!feof($error_handle) )
{
$line_of_text = fgetcsv($error_handle, 1024);
$errorName = $line_of_text[1];
$scannerName = $line_of_text[2];
if($errorName != "SCAN_RESULT" && $errorName != "" && $scannerName != "SCAN_LOGIN" && $scannerName != "")
{
$errorsArray["$errorName"]["$scannerName"]++;
}
}
fclose($error_handle);
print_r($errorsArray);
gives me the following:
Array ( [OBSOLETE_ORDER] => Array ( [obelisk] => 1 ) [WRONG_PERFORMANCE] => Array ( [g3t1] => 2 [g4t1] => 2 [g4t2] => 2 ) [DAY_LIMIT_EXCEEDED] => Array ( [obelisk] => 2 ) )
which is great...except how do I then take that apart to add to my sql database?! (I am interested in getting the key and total of that key under the key the array is under)
and then add it to the tables
-errors-
(index)id_errors
id_event
id_access_scanner
id_errors_type
total_errors
-errors_type-
(index)id_errors_type
name_errors_type
-access_scanner-
(index)id_access_scanner
id_portal
name_access_scanner
PLEASE HELP!
Thanks!
A multidimensional array is more than you need. The approach to take is to create your own string ($arrayKey in my example) to use as an array key that combines the scanner name and the error so that you can get a count.
//this is the array containing all the report lines, each as an array
$lines_of_text;
//this is going to be our output array
$errorScannerArray = array();
//this variable holds the array key that we're going to generate from each line
$arrayKey = null;
foreach($lines_of_text as $line_of_text)
{
//the array key is a combination of the scanner name and the error name
//the tilde is included to attempt to prevent possible (rare) collisions
$arrayKey = trim($line_of_text[1]).'~'.trim($line_of_text[2]);
//if the array key exists, increase the count by 1
//if it doesn't exist, set the count to 1
if(array_key_exists($arrayKey, $errorScannerArray))
{
$errorScannerArray[$arrayKey]++;
}
else
{
$errorScannerArray[$arrayKey] = 1;
}
}
//clean up
unset($line_of_text);
unset($arrayKey);
unset($lines_of_text);
//displaying the result
foreach($errorScannerArray as $errorScanner => $count)
{
//we can explode the string hash to get the separate error and scanner names
$names = explode('~', $errorScanner);
$errorName = $names[0];
$scannerName = $names[1];
echo 'Scanner '.$scannerName.' experienced error '.$errorName.' '.$count.' times'."\n";
}
$list = array();
foreach ($lines as $line) {
$values = explode(',' $line);
$error = $values[1];
$scanner = $values[2];
if (!isset($list[$error])) {
$list[$error] = array();
}
if (!isset($list[$error][$scanner])) {
$list[$error][$scanner] = 1;
} else {
$list[$error][$scanner]++;
}
}
To go through each result I just did the following:
foreach ($errorsArray as $errorName=>$info)
{
foreach ($info as $scannerName=>$total)
{
print "$errorName -> $scannerName = $total </br>";
}
}
and now will just connect it to the sql
With your edited question, this much simpler loop will work for you, you just need to then insert the data into your database inside the loop, instead of echoing it out:
$errorsArray = Array (
[OBSOLETE_ORDER] => Array (
[obelisk] => 1
)
[WRONG_PERFORMANCE] => Array (
[g3t1] => 2
[g4t1] => 2
[g4t2] => 2
)
[DAY_LIMIT_EXCEEDED] => Array (
[obelisk] => 2
)
)
foreach($errorsArray as $row => $errors) {
foreach($errors as $error => $count) {
echo $row; // 'OBSOLETE_ORDER'
echo $error; // 'obelisk'
echo $count; // 1
// insert into database here
}
}
OLD ANSWER
You just need a new array to hold the information you need, ideally a count.
Im assuming that the correct data format is:
$report = [
['157479877294','OBSOLETE_ORDER','obelisk','19/01/2013 01:42pm'],
['191532426695','WRONG_PERFORMANCE','g3t1','19/01/2013 01:56pm'],
['159523681637','WRONG_PERFORMANCE','g3t1','19/01/2013 01:57pm'],
['176481653889','WRONG_PERFORMANCE','g4t1','19/01/2013 01:57pm'],
.....
];
foreach($report as $array) {
$errorName = $array[1];
$scannerName = $array[2];
if(exists($errorsArray[$errorName][$scannerName])) {
$errorsArray[$errorName][$scannerName] = $errorsArray[$errorName][$scannerName] + 1;
}
else {
$errorsArray[$errorName][$scannerName] = 1;
}
}
I want to create an new array with duplicated MAX value from an array
and put other duplicate value in an other array
$etudiant = array ('a'=>'2','b'=>'5', 'c'=>'6', 'd'=>'6', 'e'=>'2');
and i want this result
$MaxArray = array ('c'=>'6', 'd'=>'6');
$otherarray1 = array ('a'=>'2', 'e'=>'2');
Thank you !
First, find the maximum value:
$etudiant = array ('a'=>'2','b'=>'5', 'c'=>'6', 'd'=>'6', 'e'=>'2');
$maxValue = max($etudiant);
Second, find values that appear more than once:
$dups = array_diff_assoc($etudiant, array_unique($etudiant));
Lastly, check the original arrays for values matching either $maxValue or values that are listed in $dups:
$MaxArray = $OtherArray = $ElseArray = array();
foreach ($etudiant as $key => $value) {
if ($value == $maxValue) {
$MaxArray[$key] = $value;
} else if (in_array($value, $dups)) {
$OtherArray[$key] = $value;
} else {
$ElseArray[$key] = $value;
}
}
You'll get:
$MaxArray: Array
(
[c] => 6
[d] => 6
)
$OtherArray: Array
(
[a] => 2
[e] => 2
)
Note: I wasn't sure if you wanted the $MaxArray to contain the maximum value elements only if it appears more than once in the source array. If so, just change the max call to:
$maxValue = max($dups);
You can use array_values(array_intersect($array1, $array2)) to get duplicated values, and then make a loop to capture the keys which have those values and store them into another array.
$dups = array_values(array_intersect($array1, $array2))
$max = max($dups);
$result = array();
foreach ($array1 as $key => $value){
if (in_array($value, $dups)) {
$result[$key] = $value;
}
}
foreach ($array2 as $key => $value){
if (in_array($value, $dups)) {
$result[$key] = $value;
}
}
$maxArray = array();
foreach ($dups as $key => $value) {
if ($value == $max){
$maxArray[$key] = $value;
}
}
// results are in $dups and $maxArray
If you are looking to find elements with the min and max values from an array, the following will work.
// get min keys
$min_value = min($etudiant);
$min_keys = array_keys($etudiant, $min_value);
// get max keys
$max_value = max($etudiant);
$max_keys = array_keys($etudiant, $max_value);
You could then either rebuild your example arrays with these keys in a loop. Or access them directly, i.e. $etudiant[$min_keys].
Check out the documentation for array_keys, min, max
I have an array of my inventory (ITEMS A & B)
Items A & B are sold as sets of 1 x A & 2 x B.
The items also have various properties which don't affect how they are distributed into sets.
For example:
$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK")
);
I want to redistribute the array $inventory to create $set(s) such that
$set[0] => Array
(
[0] => array(A,PINK)
[1] => array(B,RED)
[2] => array(B,BLUE)
)
$set[1] => Array
(
[0] => array(A,MAUVE)
[1] => array(B,YELLOW)
[2] => array(B,GREEN)
)
$set[2] => Array
(
[0] => array(A,ORANGE)
[1] => array(B,BLACK)
[2] => NULL
)
$set[3] => Array
(
[0] => array(A,GREY)
[1] => NULL
[2] => NULL
)
As you can see. The items are redistributed in the order in which they appear in the inventory to create a set of 1 x A & 2 x B. The colour doesn't matter when creating the set. But I need to be able to find out what colour went into which set after the $set array is created. Sets are created until all inventory is exhausted. Where an inventory item doesn't exist to go into a set, a NULL value is inserted.
Thanks in advance!
I've assumed that all A's come before all B's:
$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK")
);
for($b_start_index = 0;$b_start_index<count($inventory);$b_start_index++) {
if($inventory[$b_start_index][0] == 'B') {
break;
}
}
$set = array();
for($i=0,$j=$b_start_index;$i!=$b_start_index;$i++,$j+=2) {
isset($inventory[$j])?$temp1=$inventory[$j]:$temp1 = null;
isset($inventory[$j+1])?$temp2=$inventory[$j+1]:$temp2 = null;
$set[] = array( $inventory[$i], $temp1, $temp2);
}
To make it easier to use your array, you should make it something like this
$inv['A'] = array(
'PINK',
'MAUVE',
'ORANGE',
'GREY'
);
$inv['B'] = array(
'RED',
'BLUE',
'YELLOW',
'GREEN',
'BLACK'
);
This way you can loop through them separately.
$createdSets = $setsRecord = $bTemp = array();
$bMarker = 1;
$aIndex = $bIndex = 0;
foreach($inv['A'] as $singles){
$bTemp[] = $singles;
$setsRecord[$singles][] = $aIndex;
for($i=$bIndex; $i < ($bMarker*2); ++$i) {
//echo $bIndex.' - '.($bMarker*2).'<br/>';
if(empty($inv['B'][$i])) {
$bTemp[] = 'null';
} else {
$bTemp[] = $inv['B'][$i];
$setsRecord[$inv['B'][$i]][] = $aIndex;
}
}
$createdSets[] = $bTemp;
$bTemp = array();
++$bMarker;
++$aIndex;
$bIndex = $bIndex + 2;
}
echo '<pre>';
print_r($createdSets);
print_r($setsRecord);
echo '</pre>';
To turn your array into an associative array, something like this can be done
<?php
$inventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK")
);
$inv = array();
foreach($inventory as $item){
$inv[$item[0]][] = $item[1];
}
echo '<pre>';
print_r($inv);
echo '</pre>';
Maybe you can use this function, assuming that:
... $inventory is already sorted (all A come before B)
... $inventory is a numeric array staring at index zero
// $set is the collection to which the generated sets are appended
// $inventory is your inventory, see the assumptions above
// $aCount - the number of A elements in a set
// $bCount - the number of B elements in a set
function makeSets(array &$sets, array $inventory, $aCount, $bCount) {
// extract $aItems from $inventory and shorten $inventory by $aCount
$aItems = array_splice($inventory, 0, $aCount);
$bItems = array();
// iterate over $inventory until a B item is found
foreach($inventory as $index => $item) {
if($item[0] == 'B') {
// extract $bItems from $inventory and shorten $inventory by $bCount
// break out of foreach loop after that
$bItems = array_splice($inventory, $index, $bCount);
break;
}
}
// append $aItems and $bItems to $sets, padd this array with null if
// less then $aCount + $bCount added
$sets[] = array_pad(array_merge($aItems, $bItems), $aCount + $bCount, null);
// if there are still values left in $inventory, call 'makeSets' again
if(count($inventory) > 0) makeSets($sets, $inventory, $aCount, $bCount);
}
$sets = array();
makeSets($sets, $inventory, 1, 2);
print_r($sets);
Since you mentioned that you dont have that much experience with arrays, here are the links to the php documentation for the functions I used in the above code:
array_splice — Remove a portion of the array and replace it with something else
array_merge — Merge one or more arrays
array_pad — Pad array to the specified length with a value
This code sorts inventory without any assumption on inventory ordering. You can specify pattern (in $aPattern), and order is obeyed. It also fills lacking entries with given default value.
<?php
# config
$aInventory=array(
array("A","PINK"),
array("A","MAUVE"),
array("A","ORANGE"),
array("A","GREY"),
array("B","RED"),
array("B","BLUE"),
array("B","YELLOW"),
array("B","GREEN"),
array("B","BLACK"),
array("C","cRED"),
array("C","cBLUE"),
array("C","cYELLOW"),
array("C","cGREEN"),
array("C","cBLACK")
);
$aPattern = array('A','B','A','C');
$mDefault = null;
# preparation
$aCounter = array_count_values($aPattern);
$aCurrentCounter = $aCurrentIndex = array_fill_keys(array_unique($aPattern),0);
$aPositions = array();
$aFill = array();
foreach ($aPattern as $nPosition=>$sElement){
$aPositions[$sElement] = array_keys($aPattern, $sElement);
$aFill[$sElement] = array_fill_keys($aPositions[$sElement], $mDefault);
} // foreach
$nTotalLine = count ($aPattern);
$aResult = array();
# main loop
foreach ($aInventory as $aItem){
$sElement = $aItem[0];
$nNeed = $aCounter[$sElement];
$nHas = $aCurrentCounter[$sElement];
if ($nHas == $nNeed){
$aCurrentIndex[$sElement]++;
$aCurrentCounter[$sElement] = 1;
} else {
$aCurrentCounter[$sElement]++;
} // if
$nCurrentIndex = $aCurrentIndex[$sElement];
if (!isset($aResult[$nCurrentIndex])){
$aResult[$nCurrentIndex] = array();
} // if
$nCurrentPosition = $aPositions[$sElement][$aCurrentCounter[$sElement]-1];
$aResult[$nCurrentIndex][$nCurrentPosition] = $aItem;
} // foreach
foreach ($aResult as &$aLine){
if (count($aLine)<$nTotalLine){
foreach ($aPositions as $sElement=>$aElementPositions){
$nCurrentElements = count(array_keys($aLine,$sElement));
if ($aCounter[$sElement] != $nCurrentElements){
$aLine = $aLine + $aFill[$sElement];
} // if
} // foreach
} // if
ksort($aLine);
# add empty items here
} // foreach
# output
var_dump($aResult);
Generic solution that requires you to specify a pattern of the form
$pattern = array('A','B','B');
The output will be in
$result = array();
The code :
// Convert to associative array
$inv = array();
foreach($inventory as $item)
$inv[$item[0]][] = $item[1];
// Position counters : int -> int
$count = array_fill(0, count($pattern),0);
$out = 0; // Number of counters that are "out" == "too far"
// Progression
while($out < count($count))
{
$elem = array();
// Select and increment corresponding counter
foreach($pattern as $i => $pat)
{
$elem[] = $inv[ $pat ][ $count[$i]++ ];
if($count[$i] == count($inv[$pat]))
$out++;
}
$result[] = $elem;
}