Sorting data and print out in alphabetic order - php

I got an array which contains some data like this:
$arrs = Array("ABC_efg", "##zzAG", "#$abc", "ABC_abc")
I was trying to print the data out in this way (Printing in alphabetic order):
[String begins with character A]
ABC_abc
ABC_efg
[String begins with character other than A to Z]
#$abc
##zzAG

I'm going to assume you mean strings starting with a letter should appear before all other strings and all strings should otherwise be sorted in the standard order.
You use usort() and define a custom function for the ordering and ctype_alpha() to determine if something is a letter or not.
$arrs = Array("ABC_efg", "##zzAG", "#$abc", "ABC_abc");
usort($arrs, 'order_alpha_first');
function order_alpha_first($a, $b) {
$lenA = strlen($a);
$lenB = strlen($b);
$len = min($lenA, $lenB);
$i = 0;
while ($a[$i] == $b[$i] && $i < $len) {
$i++;
}
if ($i == $len) {
if ($lenA == $lenB) {
return 0; // they're the same
} else {
return $lenA < $lenB ? -1 : 1;
}
} else if (ctype_alpha($a[$i])) {
return ctype_alpha($b[$i]) ? strcmp($a[$i], $b[$i]) : -1;
} else {
return ctype_alpha($b[$i]) ? 1 : strcmp($a[$i], $b[$i]);
}
}
Output:
Array
(
[0] => ABC_abc
[1] => ABC_efg
[2] => #$abc
[3] => ##zzAG
)

You write a function sortArray($array, $preset=1) that splits the $array in two arrays.
($preset should be empty by default)
The first array contains all the elements that start with no special sign, the second one contains all elements that start with a special sign. You than sort the firstArray normally (sort()) and print them, and invoke the function on the second array, passing the preset.
(something like
if ($array[i][$preset] != "#") {
array_push ($firstArray ,$array[i]);
} else {
array_push ($secondArray ,$array[i]);
}
sort($firstArray);
print($firstArray);
sortArray($secondArray, $preset++);
)
It's just what came to my mind :)

Related

Compare the keys and values of three arrays with "array_diff_ukey"

Can somebody explain, what is 1 and -1 in this code: ($a>$b)?1:-1; ?
I know the Array ( [c] => blue ) is returning because the key c is not exist in $a2 and key_compare_func need to return number smaller, equal or bigger then 0.
But I don't understand, how I get the Array ( [c] => blue ), when the key_compare_func returns 0, 1 and -1:
function myfunction($a,$b) {
if ($a === $b) {
return 0;
}
return ($a > $b) ? 1 : -1;
}
$a1=array("a"=>"red","b"=>"green","c"=>"blue");
$a2=array("a"=>"blue","b"=>"black","e"=>"blue");
$result=array_diff_ukey($a1,$a2,"myfunction");
As you can see in array-diff-ukey documentation the "key_compare_func" need to return number smaller, equal or bigger then 0. the numbers 1 and -1 are just example for this results.
In your case you can simply use strcmp as it return the same logic.
You have Array ( [c] => blue ) in the return because the key c is not exist in the $a2 array as it say:
Compares the keys from array1 against the keys from array2 and returns the difference. This function is like array_diff() except the comparison is done on the keys instead of the values.
Edited
Specifically in array-diff-ukey you only need the return 0 because that the way this function is decided the keys are the same so in your example you can define it as:
function myfunction($a,$b) {
if ($a === $b)
return 0;
return 1; // or -1 or 3 or -3 **just not 0**
}
Consider that as the logic behind array-diff-ukey:
array function array-diff-ukey($a1, $a2, $compareFunction) {
$arr = array(); // init empty array
foreach($a1 as $key1 => $value1) { // for each key in a1
$found = false;
foreach($a1 as $key2 => $value2) { //search for all keys in a2
if ($compareFunction($key1, $key2) == 0)
$found = true; // found a key with the same
}
if ($found === false) // add the element only if non is found
$arr[$key1] = $value1;
}
return $arr;
}
If ($a>$b) is true (right after the ?) - you return 1. else (right after the :) will return -1.
It's a short way of writing this:
if ($a>$b) {
return 1;
} else {
return -1;
}
It is the ternary operator in PHP. You can say it as shorthand If/Else. Here is an example:
/* most basic usage */
$var = 5;
$var_is_greater_than_two = ($var > 2 ? true : false); // if $var greater than 2
// return true
// else false
If it is being difficult for you to understand, you can change it with:
if ($a===$b)
{
return 0;
}
else if($a > $b)
{
return 1;
}
else
{
return -1;
}

Why does array_diff_uassoc compares keys whose value do not match

I just read that question about strange php behaviour, and even though I could research a bit more, I'm nowhere near understanding it.
I assume the reader has read the original question and is aware of OP's code block and sample, but in short, OP is trying to compare those two arrays, and while the result is good, the compare function seems to be called erratically:
$chomik = new chomik('a');
$a = array(5, $chomik, $chomik, $chomik);
$b = array($chomik, 'b', 'c', 'd');
array_diff_uassoc($a, $b, 'compare');
The documentation is a bit obscure... but it does state that:
The comparison function must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
As I understand it, that means that the compare() function should be more like this:
function compare($a, $b) {
echo("$a : $b<br/>");
if($a === $b) return 0;
else if ($a > $b) return 1;
else return -1;
}
however this still gives very strange results, with even more "duplicates"
1 : 0
1 : 2
3 : 1
2 : 1
3 : 2
1 : 0
1 : 2
3 : 1
2 : 1
3 : 2
0 : 0
1 : 0
1 : 1
2 : 0
2 : 1
2 : 2
3 : 0
3 : 1
3 : 2
3 : 3
faced with many doubts, I read the compat php function, and the part where the check actually happens is interesting:
foreach ($args[0] as $k => $v) {
for ($i = 1; $i < $array_count; $i++) {
foreach ($args[$i] as $kk => $vv) {
if ($v == $vv) { // compare keys only if value are the same
$compare = call_user_func_array($compare_func, array($k, $kk));
if ($compare == 0) {
continue 3; // value should not be added to the result
}
}
}
}
$result[$k] = $v;
}
here's the actual source (per comment)
The way this code executes the compare function should not be outputting the result we see. Foreach is not able to move back and forth in the keys (AFAIK???), as seems to be the case in the order of the first key here:
1 : 2
3 : 1
2 : 1
moreover, it shouldn't check the keys if the value do not match, so why do all these are checked:
1 : 2
3 : 1
2 : 1
3 : 2
etc...
How can the topmost foreach() in the source code loop back and forth through the keys?!
Why are keys whose values do not match still compared?
Do foreach loops actually continue executing even when they've been continued?
Is this an example of concurrency? can call_user_func_array somehow be launched and actually execute the echo("$a : $b<br/>"); of the compare function not in the same order they were "launched"??
I believe you've pinpointed a bug, my friend. I just ran the code in the question you referenced, and sure enough, it compared keys for values that weren't the same. However, I wanted to test if the source code itself contained the mistake, so I added the official source for array_diff_uassoc this to the top his code, inside my own namespace:
<?php
namespace mine;
// Code obtained from https://pear.php.net/reference/PHP_Compat-latest/__filesource/fsource_PHP_Compat__PHP_Compat-1.6.0a3CompatFunctionarray_diff_uassoc.php.html
function array_diff_uassoc()
{
// Sanity check
$args = func_get_args();
if (count($args) < 3) {
user_error('Wrong parameter count for array_diff_uassoc()', E_USER_WARNING);
return;
}
// Get compare function
$compare_func = array_pop($args);
if (!is_callable($compare_func)) {
if (is_array($compare_func)) {
$compare_func = $compare_func[0] . '::' . $compare_func[1];
}
user_error('array_diff_uassoc() Not a valid callback ' .
$compare_func, E_USER_WARNING);
return;
}
// Check arrays
$array_count = count($args);
for ($i = 0; $i !== $array_count; $i++) {
if (!is_array($args[$i])) {
user_error('array_diff_uassoc() Argument #' .
($i + 1) . ' is not an array', E_USER_WARNING);
return;
}
}
// Compare entries
$result = array();
foreach ($args[0] as $k => $v) {
for ($i = 1; $i < $array_count; $i++) {
foreach ($args[$i] as $kk => $vv) {
if ($v == $vv) {
// echo ("$v\n");
// echo ("$vv\n");
// echo ("$k\n");
// echo ("$kk\n");
// die();
$compare = call_user_func_array($compare_func, array($k, $kk));
if ($compare == 0) {
continue 3;
}
}
}
}
$result[$k] = $v;
}
return $result;
}
class chomik {
public $state = 'normal';
public $name = 'no name';
public function __construct($name) {
$this->name = $name;
}
public function __toString() {
return $this->name . " - " . $this->state;
}
}
function compare($a, $b) {
echo("$a : $b\n");
if($a != $b) {
return 0;
}
else return 1;
}
$chomik = new chomik('a');
$a = array(5, $chomik, $chomik, $chomik);
$b = array($chomik, 'b', 'c', 'd');
array_diff_uassoc($a, $b, 'mine\compare');
This time, it only compared keys for values that were equal:
1 : 0
2 : 0
3 : 0
Strange, huh?
From this comment by powerlord:
Judging from the fact that the custom compare function asks you to return -1; 0; or 1, it seems like its doing a sort either before or at the same time as a comparison between the two arrays.
I was encouraged to go and read the actual php_array_diff() source, the registered function for array_diff_uassoc(), and discovered it uses the compare function a lot of times, which I wrongly interpreted as foreach going back and forth through the keys.
How can the topmost foreach() in the source code loop back and forth through the keys?!
it doesn't. It's just that the user-supplied function
diff_data_compare_func = php_array_user_compare;
is used multiple times to sort and evaluate the data.
[...]
zend_sort((void *) lists[i], hash->nNumOfElements,
sizeof(Bucket), diff_data_compare_func, (swap_func_t)zend_hash_bucket_swap);
[...]
while (Z_TYPE(ptrs[i]->val) != IS_UNDEF && (0 < (c = diff_data_compare_func(ptrs[0], ptrs[i])))) {
[...]
if (diff_data_compare_func(ptrs[0], ptr) != 0) {
[...]
Why are keys whose values do not match still compared?
It is true the compat pear code posted in the question hints that if the valus do not match, the compare function should not even be run, but php_array_diff() acts differently.
Do foreach loops actually continue executing even when they've been continued?
Is this an example of concurrency?
Nonsense. If I were you, I'd edit that out.

Merging numbered lists in PHP

I have a set of items with numbers already assigned to them and am trying to fill in the gaps that the person before me left in the spreadsheet. I figured I could write a php script to do this for me yet it's placing the assigned numbers in weird spots.
Here's an example:
I have an associative array of numbers / names
[0] => 3502 "Scallops, Bay" [1] => 3503 "Oysters, Chesepeake" [2] => 3504 "Clams, Cherry Stone"
The script to order these is:
$d = file("list.txt");
$j=0;
for ($i=2000;$i<8000;$i++) { //I want the codes to begin at the 2000 and end at 8000
if (strpos($d[$j], $i) !== false) {
echo $d[$j]."<br/>";
$j++;
} else {
echo $i."<br/>";
}
}
But here's what I'm getting:
2000-2056 print out fine, because they don't match [0] of $d, but then on 2057 it prints
2056
3502 "Scallops, Bay"
3503 "Oysters, Chesepeake"
2059
2060
3504 "Clams, Chery Stone"
Then goes to print on until 2080 where it prints [3] of $d.
I'm really confused. I don't see 2057 anywhere in "3502 'Scallops, Bay'"
Should I be trying a different approach?
The second argument to strpos() can be an integer or a string; if it's an integer, it's ordinal value is used to search. From the manual:
If needle is not a string, it is converted to an integer and applied as the ordinal value of a character.
You should cast the index to a string first:
if (strpos($d[$j], "$i") !== false) {
Btw, it would be better to check whether the line starts with $i and whether $d[$j] is still a valid entry:
if (isset($d[$j]) && strpos($d[$j], "$i\t") === 0) {
It's because of the order. If the script reache lets say 5000 with index of 1, it won't find 3000 with index 2.
My solution:
$A = array('3000 abc street', '2000 something', '5000 somthing other');
function ScanFor($Number, &$A) //& is realy important
{
foreach($A as $I => $V)
if(strpos($Number, $V) === 0) // so it starts with it
{
unset($A[$I]); //We don't want it anymore
list(, $Name) = explode(' ', $V, 1); //After Number there is always space, so we split it to 2 parts
return $Name;
}
return '';
}
for($I = 2000; $I < 10000; $I++)
{
printf("%d", $I);
if($Name = ScanFor($I, $A))
{
printf("\t%s", $Name)
}
printf("<br>\n");
}
Try working with the file like csv and use SplMinHeap to sort
Example:
// Where to store Result
$store = new SortedValue();
// Read File
$fp = fopen("list.txt", "r");
// Read each file content
while(($data = fgetcsv($fp, 1000, " ")) !== FALSE) {
// Filter Empty space
$data = array_filter($data);
// Your Filter
if ($data[0] > 2000 && $data[0] < 8000) {
$store->insert($data);
}
}
// Print_r all result
print_r(iterator_to_array($store));
Class Used
class SortedValue extends SplMinHeap {
function compare($a, $b) {
return $b[0] - $a[0];
}
}

Form a new string with data from an array PHP

I would need to reduce the quantity of these numbers and present them in a more concise way, instead of presenting several lines of numbers with the same "prefix" or "root". For example:
If I have an array like this, with several strings of numbers (obs: only numbers and the array is already sorted):
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
The string: 123456 is the same in all elements of the array, so it would be the root or the prefix of the number. According to the above array I would get a result like this:
//The numbers in brackets represent the sequence of the following numbers,
//instead of showing the rows, I present all the above numbers in just one row:
$stringFormed = "123456[4-5][7-9]";
Another example:
$array2 = array(
"1234",
"1235",
"1236",
"1247",
"2310",
"2311",
);
From the second array, I should get a result like this:
$stringFormed1 = "123[4-7]";
$stringFormed2 = "1247";
$stringFormed3 = "231[0-1]";
Any idea?
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
//find common string positions for all elements
$res = array();
foreach($array as $arr){
for($i=0;$i<strlen($arr);$i++){
$res[$i][$arr[$i]] = $arr[$i];
}
}
//make final string
foreach($res as $pos){
if(count($pos)==1)
$str .= implode('',$pos);
else{
//u may need to sort these values if you want them in order
$end = end($pos);
$first = reset($pos);
$str .="[$first-$end]";
}
}
echo $str; // "123456[4-5][7-9]";
Well, as I understand you want the final string with unique characters. (i'm not sure if you want it ordered)
So, first implode to create the string
$stringFormed = implode("", $array);
Then we get the unique chars :
$stringFormed=implode("",array_unique(str_split($stringFormed)));
OUTPUT: 123456789
That as a solution for first example but i didn't thought there could be several roots.
By the way i'm not sure it's well coded...
<?php
function longest_common_substring($words)
{
$words = array_map('strtolower', array_map('trim', $words));
$sort_by_strlen = create_function('$a, $b', 'if (strlen($a) == strlen($b)) { return strcmp($a, $b); } return (strlen($a) < strlen($b)) ? -1 : 1;');
usort($words, $sort_by_strlen);
// We have to assume that each string has something in common with the first
// string (post sort), we just need to figure out what the longest common
// string is. If any string DOES NOT have something in common with the first
// string, return false.
$longest_common_substring = array();
$shortest_string = str_split(array_shift($words));
while (sizeof($shortest_string)) {
array_unshift($longest_common_substring, '');
foreach ($shortest_string as $ci => $char) {
foreach ($words as $wi => $word) {
if (!strstr($word, $longest_common_substring[0] . $char)) {
// No match
break 2;
} // if
} // foreach
// we found the current char in each word, so add it to the first longest_common_substring element,
// then start checking again using the next char as well
$longest_common_substring[0].= $char;
} // foreach
// We've finished looping through the entire shortest_string.
// Remove the first char and start all over. Do this until there are no more
// chars to search on.
array_shift($shortest_string);
}
// If we made it here then we've run through everything
usort($longest_common_substring, $sort_by_strlen);
return array_pop($longest_common_substring);
}
$array = array(
"12345647",
"12345648",
"12345649",
"12345657",
"12345658",
"12345659",
);
$result= longest_common_substring($array);
for ($i = strlen($result); $i < strlen($array[0]); $i++) {
$min=intval($array[0][$i]);
$max=$min;
foreach ($array as $string) {
$val = intval($string[$i]);
if($val<$min)
$min=$val;
elseif($val>$max)
$max=$val;
}
$result.='['.$min.'-'.$max.']';
}
echo $result;
?>

php remove value from array

Hi I need help in removing values from an array using a recursive function
$array = [0] => testing,testing1
[1] => testing,testing1,testing2
[2] => testing,testing1,testing2,testing3
[3] => testing,testing1,testing2,testing3,tesing4
[4] => testing,testing1,testing2,testing3,tesing4
[5] => testing,testing1,testing2,testing3,tesing4
[6] => testing,testing1,testing2,testing3,tesing4
[7] => testing,testing1,testing2,testing3,tesing4
I need to check the array count, ie if count(array[0]) == count(array[1]),then reutrn array
else unset(array[value]);
From the above array I have to remove array[0],[1],[2] and return rest of the array values.
I've tried the below code
$idx =10;
$separtor =',';
function array_delete($idx, $array,$separtor) {
$finalvalue = array();
for ($i = 0; $i < $idx; $i++) {
$values = explode($separtor, $array[$i]);
$valuesnext = explode($separtor, $array[$i+1]);
if(count($values) != count($valuesnext) )
{
unset($array[$i]);
// reset($array);
// array_delete($idx, $array,$separtor);
if (is_array($array)) $array = array_delete($idx, $array,$separtor);
$finalvalue = $array;
}else
{
}
//echo $i;
}
return $finalvalue;
//(is_array($array)) ? array_values($array) : null;
//array_delete($idx, $array,$separtor);
}
I'm getting Notice: Undefined offset: 0 when trying calling recursive, going to infinite loop
Do you want to keep the sub-arrays that have the most items? Your descriptions appear to say this.
If so, something like the following would suffice.
// Get maximum number of items in the arrays
$max_count = max(array_map('count', $array));
// Keep only those arrays having $max_count items
$filtered = array_filter($array, function ($a) use ($max_count) {
return count($a) === $max_count;
});
Aside: if you need the filtered array to have zero-based keys, call array_values() on it.
See an example running online.
If I understand correctly, you want to filter the array such that any value in the final array is of the same length as the last element in the source array. In order to avoid mutating an array while iterating over it, this technique builds a fresh array with the elements that match your criteria.
$matchLength = count($mainArray[count($mainArray) - 1]);
$resultArray = array();
for($i = 0; $i < count($mainArray); $i++) {
if(count($mainArray[$i]) == $matchLength) {
$resultArray[] = $mainArray[$i];
}
}
If you happen to be using PHP 5.3 or greater, you can do this quicker with closures and array_filter:
$matchLength = count($mainArray[count($mainArray) - 1]);
$resultArray = array_filter($mainArray, function($element){return count($element) == $matchLength});
Double check the code, I haven't been writing PHP lately, so this is just an idea.
According to the description you gave, it could be just made (check the count of the current and the provious one, if they don't match, remove the previous one).
Example/Demo:
unset($prevKey);
$count = array();
foreach (array_keys($array) as $key) {
$count[$key] = count($array[$key]);
if (isset($prevKey) && $count[$prevKey] !== $count[$key]) {
unset($array[$prevKey]);
}
$prevKey = $key;
}
If you need to re-iterate to take removals into account, a little goto can do the job Demo:
start:
######
unset($prevKey);
$count = array();
foreach (array_keys($array) as $key) {
$count[$key] = count($array[$key]);
if (isset($prevKey) && $count[$prevKey] !== $count[$key]) {
unset($array[$prevKey]);
goto start;
###########
}
$prevKey = $key;
}

Categories