sort an array for top books in php - php

Here's an problem where i have to find the top book and the number of readers for each book in php . The array is something as below
$input=array();
$input[]=array("Harrypotter","John");
$input[]=array("Twilight","Jack");
$input[]=array("Twilight","John");
$input[]=array("Harrypotter","Jack");
$input[]=array("Gonegirl","marion");
$input[]=array("Gonegirl","marion");
$input[]=array("Gonegirl","John");
$input[]=array("Gonegirl","eliza");
i can find the top book by copying the book names onto a separate array say temparray and use the following functions
$temparray = array_count_values($temparray);
arsort($temparray);
but i'm not able to figure out on the logic how to get the number of readers for each book,the reader names may repeat so we have to eliminate the repeated ones .Any quick way to sort the thing would be helpful.

You could get each book name first to create a flat array, then apply the count. Example:
$input=array();
$input[]=array("Harrypotter","John");
$input[]=array("Twilight","Jack");
$input[]=array("Twilight","John");
$input[]=array("Harrypotter","Jack");
$input[]=array("Gonegirl","marion");
$input[]=array("Gonegirl","test");
$input[]=array("Gonegirl","John");
$input[]=array("Gonegirl","eliza");
$input = array_map('unserialize', array_unique(array_map('serialize', $input)));
// remove dups
$temparray = array_map(function($book_name){
return $book_name[0]; // get book names
}, $input);
$temparray = array_count_values($temparray); // then apply the counting
arsort($temparray);
print_r($temparray); // Array ( [Gonegirl] => 3 [Twilight] => 2 [Harrypotter] => 2 )

$input=array();
$input[]=array("Harrypotter","John");
$input[]=array("Twilight","Jack");
$input[]=array("Twilight","John");
$input[]=array("Harrypotter","Jack");
$input[]=array("Gonegirl","marion");
$input[]=array("Gonegirl","marion");
$input[]=array("Gonegirl","John");
$input[]=array("Gonegirl","eliza");
$hash = array();
$sorted = array();
array_walk($input, function ($v) use (&$hash, &$sorted) {
$book = $v[0];
if (!in_array(implode('_', $v), $hash)) {
$hash[] = implode('_', $v);
!isset($sorted[$v[0]])?$sorted[$v[0]]=1:$sorted[$v[0]]++;
}
});
arsort($sorted);
print_r($sorted);

Related

Sort a flat array in recurring ascending sequences

I am trying to sort it in a repeating, sequential pattern of numerical order with the largest sets first.
Sample array:
$array = [1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3];
In the above array, I have the highest value of 5 which appears twice so the first two sets would 1,2,3,4,5 then it would revert to the second, highest value set etc.
Desired result:
[1,2,3,4,5,1,2,3,4,5,1,2,3,4,1,2,4]
I am pretty sure I can split the array into chunks of the integer values then cherrypick an item from each subarray sequentially until there are no remaining items, but I just feel that this is going to be poor for performance and I don't want to miss a simple trick that PHP can already handle.
Here's my attempt at a very manual loop using process, the idea is to simply sort the numbers into containers for array_unshifting. I'm sure this is terrible and I'd love someone to do this in five lines or less :)
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
// Build the container array
$numbers = array_fill_keys(array_unique($array),array());
// Assignment
foreach( $array as $number )
{
$numbers[ $number ][] = $number;
}
// Worker Loop
$output = array();
while( empty( $numbers ) === false )
{
foreach( $numbers as $outer => $inner )
{
$output[] = array_shift( $numbers[ $outer ] );
if( empty( $numbers[ $outer ] ) )
{
unset( $numbers[ $outer ] );
}
}
}
var_dump( $output );
I think I'd look at this not as a sorting problem, but alternating values from multiple lists, so rather than coming up with sets of distinct numbers I'd make sets of the same number.
Since there's no difference between one 1 and another, all you actually need is to count the number of times each appears. It turns out PHP can do this for you with aaray_count_values.
$sets = array_count_values ($input);
Then we can make sure the sets are in order by sorting by key:
ksort($sets);
Now, we iterate round our sets, counting down how many times we've output each number. Once we've "drained" a set, we remove it from the list, and once we have no sets left, we're all done:
$output = [];
while ( count($sets) > 0 ) {
foreach ( $sets as $number => $count ) {
$output[] = $number;
if ( --$sets[$number] == 0 ) {
unset($sets[$number]);
}
}
}
This algorithm could be adapted for cases where the values are actually distinct but can be put into sets, by having the value of each set be a list rather than a count. Instead of -- you'd use array_shift, and then check if the length of the set was zero.
You can use only linear logic to sort using php functions. Here is optimized way to fill data structures. It can be used for streams, generators or anything else you can iterate and compare.
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
$chunks = [];
$index = [];
foreach($array as $i){
if(!isset($index[$i])){
$index[$i]=0;
}
if(!isset($chunks[$index[$i]])){
$chunks[$index[$i]]=[$i];
} else {
$chunks[$index[$i]][] = $i;
}
$index[$i]++;
}
$result = call_user_func_array('array_merge', $chunks);
print_r($result);
<?php
$array = array(1,1,1,2,3,2,3,4,5,4,4,4,5,1,2,2,3);
sort($array);
while($array) {
$n = 0;
foreach($array as $k => $v) {
if($v>$n) {
$result[] = $n = $v;
unset($array[$k]);
}
}
}
echo implode(',', $result);
Output:
1,2,3,4,5,1,2,3,4,5,1,2,3,4,1,2,4
New, more elegant, more performant, more concise answer:
Create a sorting array where each number gets its own independent counter to increment. Then use array_multisort() to sort by this grouping array, then sort by values ascending.
Code: (Demo)
$encounters = [];
foreach ($array as $v) {
$encounters[] = $e[$v] = ($e[$v] ?? 0) + 1;
}
array_multisort($encounters, $array);
var_export($array);
Or with a functional style with no global variable declarations: (Demo)
array_multisort(
array_map(
function($v) {
static $e;
return $e[$v] = ($e[$v] ?? 0) + 1;
},
$array
),
$array
);
var_export($array);
Old answer:
My advice is functionally identical to #El''s snippet, but is implemented in a more concise/modern/attractive fashion.
After ensuring that the input array is sorted, make only one pass over the array and push each re-encountered value into its next row of values. The $counter variable indicates which row (in $grouped) the current value should be pushed into. When finished looping and grouping, $grouped will have unique values in each row. The final step is to merge/flatten the rows (preserving their order).
Code: (Demo)
$grouped = [];
$counter = [];
sort($array);
foreach ($array as $v) {
$counter[$v] = ($counter[$v] ?? -1) + 1;
$grouped[$counter[$v]][] = $v;
}
var_export(array_merge(...$grouped));

php foreach group results by 3 sets of characters

I am trying to group some foreach results based on the first 3 sets of characters.
For example i am currently listing sku codes for products and they look like this:
REF-MUSBOM-0500-ORA
REF-PROCOF-0001-LAT
REF-WHEREF-0001-TRO
REF-WHEREF-0001-ORA
REF-SHAKER-0700-C/B
REF-CREMON-0100-N/A
REF-GLUSUL-0090-N/A
REF-CRECAP-0090-N/A
REF-ALBFER-0120-N/A
REF-TSHCOT-LARG-BLK
REF-TSHCOT-MEDI-BLK
REF-ALBMAG-0090-N/A
REF-GYMJUG-2200-N/A
REF-OMEGA3-0090-N/A
REF-NEXGEN-0060-N/A
REF-VITAD3-0100-N/A
REF-SSSHAK-0739-N/A
REF-GINKGO-0090-N/A
REF-DIGEZY-0090-N/A
REF-VEST00-MEDI-N/A
REF-VEST00-LARG-N/A
REF-CREMON-0250-N/A
REF-MSM----0250-N/A
REF-GRNTEA-0100-N/A
REF-COLOST-0100-N/A
REF-GLUCHO-0090-N/A
REF-ZINCMA-0100-N/A
REF-BETALA-0250-N/A
REF-DRIBOS-0250-N/A
REF-HMB000-0090-N/A
REF-ALACID-0090-N/A
REF-CLA000-0090-N/A
REF-ACETYL-0090-N/A
REF-NXGPRO-0090-N/A
REF-LGLUTA-0250-N/A
REF-BCAA20-0200-N/A
REF-FLAPJA-0012-ACR
REF-FLAPJA-0012-MAP
REF-LCARNI-0100-N/A
REF-CORDYC-0090-N/A
REF-CREMON-0500-N/A
REF-BCAAEN-0330-APP
REF-PREWKT-0300-FPU
REF-TESFUS-0090-N/A
REF-AMIIFUS-0300-GAP
REF-AMIIFUS-0300-WME
REF-BCAINT-0400-FPU
REF-KRILLO-0090-N/A
REF-AMIIFUS-0300-PLE
REF-AMIIFUS-0300-FPU
REF-BCAINT-0400-WME
REF-ENZQ10-0090-N/A
REF-THERMO-0100-N/A
REF-LGLUTA-0500-N/A
REF-RBAR00-0012-DCB
REF-RBAR00-0012-PBC
REF-RBAR00-0012-WCR
REF-IMHEAV-2200-CHO
REF-PROCOF-0012-N/A
REF-DIEPRO-0900-STR
REF-DIEPRO-0900-BOF
REF-DIEPRO-0900-CHO
REF-INWPRO-0900-VAN
REF-INWPRO-0900-BOF
REF-INWPRO-0900-BCS
REF-INWPRO-0900-CHO
REF-INWPRO-0900-CMI
REF-INWPRO-0900-RAS
REF-INWPRO-0900-STR
REF-INWPRO-0900-CIN
REF-INWPRO-0900-CPB
REF-EGGPRO-0900-CHO
REF-EGGPRO-0900-VAN
REF-MICCAS-0909-CHO
REF-MICCAS-0909-CMI
REF-MICCAS-0909-VAN
REF-MICCAS-0909-STR
REF-BCAA50-0500-N/A
REF-MICWHE-0909-STR
REF-MICWHE-0909-VAN
REF-MICWHE-0909-CHIO
REF-MICWHE-0909-BAN
REF-1STOXT-2030-STR
REF-1STOXT-2030-VAN
REF-1STOXT-2030-CHO
REF-MUSBOM-0600-BCH
REF-MUSBOM-0600-FPU
REF-MUSBCF-0600-BCH
REF-MUSBCF-0600-FPU
REF-VEGANP-2100-STR
REF-VEGANP-2100-CHO
REF-INMPRO-2270-CPB
REF-DIETMR-2400-CPB
REF-INMPRO-2270-SCR
REF-INMPRO-2270-VIC
REF-MATRIX-1800-FRU
REF-INMPRO-2270-BOF
REF-MATRIX-1800-CHO
REF-INMPRO-2270-CHO
REF-ONESTO-2100-CHO
In the above list there are 2 skus which are:
REF-WHEREF-0001-TRO
REF-WHEREF-0001-ORA
The first 3 sets of characters split by - are the same. What would be the best approach of grouping all results leaving me an array something like this:
Array
(
[REF-WHEREF-0001] => Array
(
[0] => REF-WHEREF-0001-TRO
[1] => REF-WHEREF-0001-ORA
)
)
Are the first 3 groups (excluding the multiple -) always 13 characters? Then do something like this:
<?php
$arr = ["REF-MUSBOM-0500-ORA",
"REF-PROCOF-0001-LAT",
"REF-WHEREF-0001-TRO",
"REF-WHEREF-0001-PPL"];
$resultArr = [];
foreach ($arr as $sku) {
$resultArr[substr($sku, 0, 15)][] = $sku;
}
var_dump($resultArr);
If that length varies you might want to work with a regex or the strpos() of the third -.
I must say that I think you could come up with this yourself, since you were already thinking in the right direction i.e. foreach()
EDIT: Because I found other solutions more elegant looking, I decided to compare efficiency. This solution is a lot faster than the other ones.
I always create a new array with the index that I need for group, try this:
$arr=array('REF-MUSBOM-0500-ORA',
'REF-PROCOF-0001-LAT',
'REF-WHEREF-0001-TRO');
$newarr=array();
foreach($arr as $a){
$b=explode('-',$a);
array_pop($b);
$b=implode("-", $b);
$newarr[$b][]=$a;
}
echo '<pre>',print_r($newarr),'</pre>';
You will need to pick a group with some basics use of explode, implode and str_replace.
What does this solution do.
loop through the array of your items
explode to get last item index of exploded string assuming that it
would be dynamic in the end
implode & str_replace again to find out string of group name
And last strpos & in_array to have sample reponse
Solution
$array = array(
'REF-MUSBOM-0500-ORA',
'REF-PROCOF-0001-LAT',
'REF-WHEREF-0001-TRO',
'REF-WHEREF-0001-ORA',
'REF-SHAKER-0700-C/B',
'REF-CREMON-0100-N/A',
'REF-GLUSUL-0090-N/A',
'REF-CRECAP-0090-N/A',
'REF-ALBFER-0120-N/A',
);
$new_array = array();
foreach ($array as $key => $val) {
$group_arr = explode('-', $val);
$end = end($group_arr);
$combined_group = implode('-', $group_arr);
$group = str_replace('-' . $end, '', $combined_group);
if (strpos($val, $group) !== false && !in_array($group, $new_array)) {
$new_array[$group][] = $val;
}
}
echo '<pre>';print_r($new_array);echo '</pre>';
See demo on Sandbox

Need help to order strings by their internal value

I am using a text file as little database, and each line has this format:
3692|Giovanni|giojo982|0005405
9797|Stefano|stefy734|45367
2566|Marco|markkkk998|355647689
4721|Roberto|robn88|809678741
I need to order them alphabetically maintaining their indexes, if it's possible.
At the moment, I'm using this code, but in this situation logically it doesn't work any more.
Reading this post How can I sort arrays and data in PHP? I have not found nothing similar to my scenario.
So, I'm wondering... is there a solution?
$db_friends = "db_friends.txt";
$dblines = file($db_friends);
if(isset($_GET['A-Z'])) {
asort($dblines);
}
if(isset($_GET['Z-A'])) {
arsort($dblines);
}
foreach($dblines as $key => $profile) {
list($uni_id, $name_surname, $textnum_id, $num_id) = explode("|", $profile);
echo $name_surname;
}
A-Z
Z-A
How can I solve it?
I assume by alphabetically that you're trying to sort alphabetically by the name in the second column. The problem is, asort() and arsort() perform too simple a comparison to deal with the type of data you're giving them. They're just going to see the rows as strings, and sort by the number in the first column. One way to address this is by splitting the rows before sorting.
$db_friends = "db_friends.txt";
$dblines = file($db_friends);
// split each line into an array
$dblines = array_map(function($line){
return explode('|', $line);
}, $dblines);
Then you can sort by the second column more easily. Using uasort(), you'll maintain the index association.
if (isset($_GET['A-Z'])) {
uasort($dblines, function(array $a, array $b) {
return strcmp($a[1], $b[1]);
});
}
if (isset($_GET['Z-A'])) {
uasort($dblines, function(array $a, array $b) {
return strcmp($b[1], $a[1]);
});
}
Obviously if you make that change, you'll no longer need to explode when you iterate the sorted array, as it was already done in the first step.
foreach ($dblines as $key => $profile) {
list($uni_id, $name_surname, $textnum_id, $num_id) = $profile;
echo $name_surname;
}
You can avoid the complexity of a uasort() call if you just position your columns in the order that you want to alphabetize them. This method has the added benefit of sorting all column data from left to right. This means, regardless of sorting direction (asc or desc), my method will sort $rows[0], then $row[1], then $row[2], then $row[3].
I have also logically combined your two if statements and set ASC as the default sorting direction.
Code: (Demo)
$txt=['9999|Marco|markkkk998|355647689','1|Marco|markkkk998|355647689','3692|Giovanni|giojo982|0005405','9797|Stefano|stefy734|45367','2566|Marco|markkkk998|355647689','4721|Roberto|robn88|809678741'];
foreach($txt as $row){
$values=explode('|',$row);
$rows[]=[$values[1],$values[0],$values[2],$values[3]]; // just reposition Name column to be first column
}
if(isset($_GET['Z-A'])) {
arsort($rows); // this will sort each column from Left-Right using Z-A
}else{
asort($rows); // (default) this will sort each column from Left-Right using A-Z
}
// var_export($rows);
foreach($rows as $i=>$profile) {
echo "$i {$profile[0]}\n"; // name value
}
Output:
2 Giovanni
1 Marco
4 Marco
0 Marco
5 Roberto
3 Stefano
If I understood your question, you could try this function:
function sortValuesKeepKey($lines) {
//declare arrays to be used temporarily
$idNumbers = array();
$values = array();
$return = array();
//loop through each line and seperate the number at the beginning
//of the string from the values into 2 seperate arrays
foreach($lines as $line) {
$columns = explode("|", $line);
$id = array_shift($columns);
$idNumbers[] = $id;
$values[] = implode("|", $columns);
}
//sort the values without the numbers at the beginning
asort($values);
//loop through each value and readd the number originally at the beginning
//of the string
foreach($values as $key => $value) {
//use $key here to ensure your putting the right data back together.
$return[$key] = $idNumbers[$key]."|".$values[$key];
}
//return finished product
return $return;
}
Just pass it the lines as an array and it should return it ordered properly.
If you want to sort the values by name_surname, see the below code
$db_friends = "db_friends.txt";
$dblines = file($db_friends);
// loop the lines
foreach($dblines as $key => $profile) {
// explode each line with the delimiter
list($uni_id, $name_surname, $textnum_id, $num_id) = explode("|", $profile);
// create an array with name_surname as a key and the line as value
$array[$name_surname] = $profile;
}
// bases on the GET paramater sort the array.
if(isset($_GET['A-Z'])) {
ksort($array); //sort acceding
}
if(isset($_GET['Z-A'])) {
krsort($array); // sort descending
}
// loop the sorted array
foreach($array as $key => $value) {
echo $key; // display the name_surname.
}

How to group numbers in an array in php

I have a loads of numbers in a string
$userlist = '12,17,46,35,32,66,43,64'; //the userlist can be as long as i want
$arr2 = explode(',', $userlist);
i dont now how to get them to output like the follow.
12,17
46,35
32,66
43,64
Thank You for taking the time to read.
You can use array_chunk to group two together again and then iterate over the groups and implode each again with the ,. Example (Demo):
$userPairings = array_chunk($arr2, 2);
foreach ($userPairings as &$pair)
{
$pair = implode(',', $pair);
}
unset($pair);
echo implode("\n\n", $userPairings);
Output:
12,17
46,35
32,66
43,64

PHP merge array(s) and delete double values

WP outputs an array:
$therapie = get_post_meta($post->ID, 'Therapieen', false);
print_r($therapie);
//the output of print_r
Array ( [0] => Massagetherapie )
Array ( [0] => Hot stone )
Array ( [0] => Massagetherapie )
How would I merge these arrays to one and delete all the exact double names?
Resulting in something like this:
theArray
(
[0] => Massagetherapie
[1] => Hot stone
)
[SOLVED] the problem was if you do this in a while loop it wont work here my solution, ty for all reply's and good code.: Its run the loop and pushes every outcome in a array.
<?php query_posts('post_type=therapeut');
$therapeAr = array(); ?>
<?php while (have_posts()) : the_post(); ?>
<?php $therapie = get_post_meta($post->ID, 'Therapieen', true);
if (strpos($therapie,',') !== false) { //check for , if so make array
$arr = explode(',', $therapie);
array_push($therapeAr, $arr);
} else {
array_push($therapeAr, $therapie);
} ?>
<?php endwhile; ?>
<?php
function array_values_recursive($ary) { //2 to 1 dim array
$lst = array();
foreach( array_keys($ary) as $k ) {
$v = $ary[$k];
if (is_scalar($v)) {
$lst[] = $v;
} elseif (is_array($v)) {
$lst = array_merge($lst,array_values_recursive($v));
}}
return $lst;
}
function trim_value(&$value) //trims whitespace begin&end array
{
$value = trim($value);
}
$therapeAr = array_values_recursive($therapeAr);
array_walk($therapeAr, 'trim_value');
$therapeAr = array_unique($therapeAr);
foreach($therapeAr as $thera) {
echo '<li><input type="checkbox" value="'.$thera.'">'.$thera.'</input></li>';
} ?>
The following should do the trick.
$flattened = array_unique(call_user_func_array('array_merge', $therapie));
or the more efficient alternative (thanks to erisco's comment):
$flattened = array_keys(array_flip(
call_user_func_array('array_merge', $therapie)
));
If $therapie's keys are strings you can drop array_unique.
Alternatively, if you want to avoid call_user_func_array, you can look into the various different ways of flattening a multi-dimensional array. Here are a few (one, two) good questions already on SO detailing several different methods on doing so.
I should also note that this will only work if $therapie is only ever a 2 dimensional array unless you don't want to flatten it completely. If $therapie is more than 2 dimensions and you want to flatten it into 1 dimension, take a look at the questions I linked above.
Relevant doc entries:
array_flip
array_keys
array_merge
array_unique
call_user_func_array
It sounds like the keys of the arrays you are generating are insignificant. If that's the case, you can do a simple merge then determine the unique ones with built in PHP functions:
$array = array_merge($array1, $array2, $array3);
$unique = array_unique($array);
edit: an example:
// Emulate the result of your get_post_meta() call.
$therapie = array(
array('Massagetherapie'),
array('Hot stone'),
array('Massagetherapie'),
);
$array = array();
foreach($therapie as $thera) {
$array = array_merge($array, $thera);
}
$unique = array_unique($array);
print_r($unique);
PHP's array_unique() will remove duplicate values from an array.
$tester = array();
foreach($therapie as $thera) {
array_push($tester, $thera);
}
$result = array_unique($tester);
print_r($result);

Categories