Sort Array by combining orders from multiple Arrays - php

I'm making a simple search engine, and I have already indexed a lot of websites in a MySQL database. Now I would like to get a relevant list of results by keywords.
Websites are indexed in my database with the following columns : hostname (without protocol an port), title, description. (We don't care about path)
When I type some keywords on my search engine homepage, it first starts by fetching 50 websites using FULLTEXT indexes.
Now, and because using Levenshtein algorithm in MySQL is really slow, I would like to sort those results with the Levenshtein PHP function for each columns I listed previously.
I would like to sort them in this order (most important first) : hostname, title, and then description.
So I have five arrays :
$results, returned by MySQL
$sorted_by_mysql, containing keys of $results in the original order : 0, 1, 2, ...
$sorted_by_hostname, containing keys of $results sorted by hostname's relevance using Levenshtein, ex: 3, 0, 1, 2, ...
$sorted_by_title, containing keys of $results sorted by title's relevance using Levenshtein, ex: 0, 2, 1, 3, ...
$sorted_by_description, containing keys of $results sorted by description's relevance using Levenshtein, ex: 1, 3, 0, 2, ...
Here's the code :
$results = $req->fetchAll();
$search = strtolower($q);
$temp_arr = [];
$sorted_by_mysql = $sorted_by_hostname = $sorted_by_title = $sorted_by_description = [];
// We keep the original order in an array
for($i = 0; $i < count($results); $i++) $sorted_by_mysql[] = $i;
// Sort by hostname
for($i = 0; $i < count($results); $i++) $temp_arr[$i] = levenshtein($search, strtolower($results[$i]->hostname));
asort($temp_arr);
foreach($temp_arr as $k => $v) $sorted_by_hostname[] = $k;
// Sort by title
for($i = 0; $i < count($results); $i++) $temp_arr[$i] = levenshtein($search, strtolower($results[$i]->title));
asort($temp_arr);
foreach($temp_arr as $k => $v) $sorted_by_title[] = $k;
// Sort by description
for($i = 0; $i < count($results); $i++) $temp_arr[$i] = levenshtein($search, strtolower($results[$i]->description));
asort($temp_arr);
foreach($temp_arr as $k => $v) $sorted_by_description[] = $k;
Finally I would like to sort $results by combining (by priority) all thoses different arrays. But I have no idea on how, so here's where I need some help !
EDIT : Solution !
$data = $req->fetchAll();
$search = strtolower($q);
$temp = [];
foreach($data as $i => $row) {
$temp[] = [
'id' => $i,
'lev1' => levenshtein($search, strtolower($row->hostname)),
'lev2' => levenshtein($search, strtolower($row->title)),
'lev3' => levenshtein($search, strtolower($row->description))
];
}
$sorted = array_orderby($temp, 'lev1', SORT_ASC, 'lev2', SORT_ASC, 'lev3', SORT_ASC, 'id', SORT_ASC);
$results = [];
foreach($sorted as $row) {
$results[] = $data[$row['id']];
}
// Perfectly sorted !
Here's array_orderby function :
// Credits : jimpoz at jimpoz dot com (PHP.net)
function array_orderby()
{
$args = func_get_args();
$data = array_shift($args);
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);
}

See the answer to this SO question, they have a similar need but have structured their data in a way that makes the answer easier. It looks like PHP supports sorting by multiple attributes (in descending priority) as long as those attributes are built into the associative array that's being sorted.
To apply this approach to your data, you'll probably want to restructure your results into one giant associative array where each element of the array contains a value for each "field" you're aiming to sort by. Does that make sense?
Good luck!

Related

PHP array comparision error

I have 2 SELECT statement in my PHP. Both the select statements fetch data from two different DB. The fetched data is saved in PDO Assoc Array. The problem is when I want to compare those two arrays to find that if the column 'id' exist in both arrays or not. If it exists then ignore it. If it's a unique id then save it into a third array. But I found some problems in my Logic Below
And after running the below code I am getting a couple of error:
1: Array to string conversion
2: duplicate key value violates unique constraint
$arr1 = $msql->fetchAll(PDO::FETCH_ASSOC);
$array1 = array();
foreach($arr1 as $x){
$array1[] = $x['id'];
}
$arr2 = $psql->fetechAll(PDO::FETCH_ASSOC);
$array2 = array();
foreach($arr2 as $y){
$array2[] = $y['id'];
}
$finalarray = array();
for ($i = 0; $i < count($arr1); $i++){
if(count(array_intersect($array1,$array2)) <= 1){// is the count of id is 1 or less save that whole row in the $finalarray
$finalarray = $arr1[$i]; // saving the unique row.
}
else{
continue;
}
}
All I am trying to get the unique row of data array() after comparing their id column.
You can use in_array() function as both arrays are index array.
$finalarray = array();
for ($i = 0; $i < count($arr1); $i++){
if(count(array_intersect($array1,$array2)) <= 1){// is the count of id is 1 or less save that whole row in the $finalarray
$finalarray = $arr1[$i]; // saving the unique row.
}
else{
continue;
}
}
make change in code:
$finalarray = array();
for ($i = 0; $i < count($arr1); $i++){
if(!in_array($array1[$i], $array2)){
$finalarray[] = $array1[$i]; // saving the unique row.
}
}
You can simply use array_intersect() to get common values between two array. for difference, can use array_diff()
$array1 = [1,2,3,4,5,6,7];
$array2 = [2,4,6];
//array_intersect — Computes the intersection of arrays
$result = array_intersect($array1, $array2);
print_r($result);
//array_diff — Computes the difference of arrays
$result = array_diff($array1, $array2);
print_r($result);
DEMO
Rather than using 3 different arrays to get unique ids, you can do it by using one array. Make changes to your code as below:
$finalarray = array();
$arr1 = $msql->fetchAll(PDO::FETCH_ASSOC);
foreach($arr1 as $x){
if (!in_array($x['id'],$finalarray)) { // check id is already stored or not
$finalarray[] = $x['id'];
}
}
$arr2 = $psql->fetechAll(PDO::FETCH_ASSOC);
foreach($arr2 as $y){
if (!in_array($y['id'],$finalarray)) { // check id is already stored or not
$finalarray[] = $y['id'];
}
}
Maybe you should make sure which array is larger before your loop ;
Or using array_diff:
$finalarray = array_diff($array1 , $array2) ?? [];
$finalarray = array_merge( array_diff($array2 , $array1) ?? [], $finalarray );

combine array entries with every other entry

Sorry for the title as it looks like most of the other questions about combining arrays, but I don't know how to write it more specific.
I need a PHP function, which combines the entries of one array (dynamic size from 1 to any) to strings in every possible combination.
Here is an example with 4 entries:
$input = array('e1','e2','e3','e4);
This should be the result:
$result = array(
0 => 'e1',
1 => 'e1-e2',
2 => 'e1-e2-e3',
3 => 'e1-e2-e3-e4',
4 => 'e1-e2-e4',
5 => 'e1-e3',
6 => 'e1-e3-e4',
7 => 'e1-e4'
8 => 'e2',
9 => 'e2-e3',
10 => 'e2-e3-e4',
11 => 'e2-e4',
12 => 'e3',
13 => 'e3-e4',
14 => 'e4'
);
The sorting of the input array is relevant as it affects the output.
And as you see, there should be an result like e1-e2 but no e2-e1.
It seems really complicated, as the input array could have any count of entries.
I don't even know if there is a mathematical construct or a name which describes such a case.
Has anybody done this before?
You are saying that there might be any number of entries in the array so I'm assuming that you aren't manually inserting the data and there would be some source or code entering the data. Can you describe that? It might be easier to directly store it as per your requirement than having an array and then changing it as per your requirement
This might be helpful Finding the subsets of an array in PHP
I have managed to bodge together a code that creates the output you want from the input you have.
I think I have understood the logic of when and why each item looks the way it deos. But Im not sure, so test it carefully before using it live.
I have a hard time explaining the code since it's really a bodge.
But I use array_slice to grab the values needed in the strings, and implode to add the - between the values.
$in = array('e1','e2','e3','e4');
//$new =[];
$count = count($in);
Foreach($in as $key => $val){
$new[] = $val; // add first value
// loop through in to greate the long incrementing string
For($i=$key; $i<=$count-$key;$i++){
if($key != 0){
$new[] = implode("-",array_slice($in,$key,$i));
}else{
if($i - $key>1) $new[] = implode("-",array_slice($in,$key,$i));
}
}
// all but second to last except if iteration has come to far
if($count-2-$key >1) $new[] = Implode("-",Array_slice($in,$key,$count-2)). "-". $in[$count-1];
// $key (skip one) next one. except if iteration has come to far
If($count-2-$key >1) $new[] = $in[$key] . "-" . $in[$key+2];
// $key (skip one) rest of array except if iteration has come to far
if($count-2-$key > 1) $new[] = $in[$key] ."-". Implode("-",Array_slice($in,$key+2));
// $key and last item, except if iteration has come to far
if($count-1 - $key >1) $new[] = $in[$key] ."-". $in[$count-1];
}
$new = array_unique($new); // remove any duplicates that may have been created
https://3v4l.org/uEfh6
here is a modificated version of Finding the subsets of an array in PHP
function powerSet($in,$minLength = 1) {
$count = count($in);
$keys = array_keys($in);
$members = pow(2,$count);
$combinations = array();
for ($i = 0; $i < $members; $i++) {
$b = sprintf("%0".$count."b",$i);
$out = array();
for ($j = 0; $j < $count; $j++) {
if ($b{$j} == '1') {
$out[] = $keys[$j];
}
}
if (count($out) >= $minLength) {
$combinations[] = $out;
}
}
$result = array();
foreach ($combinations as $combination) {
$values = array();
foreach ($combination as $key) {
$values[$key] = $in[$key];
}
$result[] = implode('-', $values);
}
sort($result);
return $result;
}
This seems to work.

Retrieve Highest Value from Array in PHP

I would post the entire code, but it is lengthly and confusing, so I'll keep it short and simple. This is complicated for myself, so any help will be greatly appreciated!
These are the values from my Array:
Light Blue1
Blue2
Blue1
Black3
Black2
Black1
The values I need to retrieve from my Array are "Light Blue1", "Blue2" and "Black3". These are the "highest values" for each color.
Something similar to what I'm looking for is array_unique, but that wouldn't work here. So something along those lines that can retrieve each color with its highest number.
Thanks!
Assuming your format is always NameNumber a regex should do the trick for separating the data. This will loop through your data in the order your provide and grab the first element that is different and put it into $vals. I am also assuming your data will always be ordered as your example shows
$data = ['Light Blue1',
'Blue2',
'Blue1',
'Black3',
'Black2',
'Black1'];
$vals = [];
$current = '';
foreach($data as $row) {
if(!preg_match('/(.*)(\d)/i', $row, $matched)) continue;
if($matched[1] != $current) {
$vals[] = $row;
$current = $matched[1];
}
}
The solution using preg_split and max functions:
$colors = ['Light Blue1', 'Blue2', 'Blue1', 'Black3', 'Black2', 'Black1'];
$unique_colors = $result = [];
foreach ($colors as $k => $v) {
$parts = preg_split("/(\d+)/", $v, 0, PREG_SPLIT_DELIM_CAPTURE);
$unique_colors[$parts[0]][] = (int) $parts[1];
}
foreach ($unique_colors as $k => $v) {
$result[] = $k . max($v);
}
print_r($result);
The output:
Array
(
[0] => Light Blue1
[1] => Blue2
[2] => Black3
)
If you pre-sort your array with "natural sorting", then you can loop through the array and unconditionally push values into the result with digitally-trimmed keys. This will effectively overwrite color entries with lesser number values and only store the the highest numbered color when the loop finishes.
Code: (Demo)
natsort($data);
$result = [];
foreach ($data as $value) {
$result[rtrim($value, '0..9')] = $value;
}
var_export(array_values($result));
Or you could parse each string and compare the number against its cached number (if encountered before): (Demo)
$result = [];
foreach ($data as $value) {
sscanf($value, '%[^0-9]%d', $color, $number);
if (!isset($result[$color]) || $result[$color]['number'] < $number) {
$result[$color] = ['value' => $value, 'number' => $number];
}
}
var_export(array_column($result, 'value'));
A related technique to find the highest value in a group

PHP build array from variables

So I have a variable which I explode:
$values = explode ('|', $split);
This can contain any number of values from 1 to 10+
I have another big array let's call it $tree. I need to loop round the $values whilst building up an array based on the $tree variable.
E.g:
$newArray = $tree [$values [0]][$values [1]];
But this needs to be done dynamically based on the number of elements in the $values array.
Any ideas?
Thanks
Is this what you're trying to do?
$newArray = array();
foreach($values as $key => $val)
{
$newArray[] = $tree[$val][$values[$key + 1]];
}
You need a foreach loop that goes to every single value you have and then put them in the $tree array something like:
$newArray = array();
foreach($values as $index => $value)
{
$newArray[] = $tree[$value][$value[$index + 1]];
}
create a temporary array from $tree and iterate through the values getting each index:
$result = $tree;
foreach ($values as $val){
$result = $result[$val];
}
This way you go down one level deeper into $tree with each value supplied in $values, and $result holds the value stored in $tree at the point you have reached. For example if you have a navigation tree, $values would be the "breadcrumb" of the current navigation position, and $result is the remaining tree from this point downwards.
I think this is what you want. It goes through pairs of elements of $values, using them as the indexes into $tree to add to $newArray
$newArray = array();
for ($i = 0; $i < count(values); $i += 2) {
$newArray[] = $tree[$values[$i]][$values[$i+1]];
}
$values=array(0, 1, 3);
$tree=array("First", "Second", "Third", "Fourth");
$newarray=array();
for ($i=0; $i<count($values); $i++)
{
$newarray[]=$tree[$values[$i]];
}
echo(implode($newarray,", "));
Something like that what you were looking for?

Creating a ranked list from multiple arrays

I have 3 arrays that return a url,title,snippet and score from 3 different search engines, the score starts at 100 for the element in the array, the second 99 and so on, I'm trying to combine all 3 into one array. If the urls match from the different arrays I want to add the scores together and then delete the duplicate url. If there is no match between the urls then I just want to put this element into the combined array.
The final combined list should contain all distinct urls with its score,title and snippet, here are my array structures
googleArray
$x=0;
$score=100;
foreach ($js->items as $item)
{
$googleArray[$x]['link'] = ($item->{'link'});
$googleArray[$x]['title'] = ($item->{'title'});
$googleArray[$x]['snippet'] = ($item->{'snippet'});
$googleArray[$x]['score'] = $score--;
$x++;
}
blekkoArray
$score = 100;
foreach ($js->RESULT as $item)
{
$blekkoArray[$i]['url'] = ($item->{'url'});
$blekkoArray[$i]['title'] = ($item->{'url_title'});
$blekkoArray[$i]['snippet'] = ($item->{'snippet'});
$blekkoArray[$i]['score'] = $score--; // assign the $score value here
$i++;
}
bingArray
foreach($jsonObj->d->results as $value)
{ $i = 0;
$bingArray[]['Url'] = ($value->{'Url'});
$bingArray[]['Title'] = ($value->{'Title'});
$bingArray[]['Description'] = ($value->{'Description'});
$bingArray[]['score'] = $score--;
$i++;
}
Any help would be great, thanks in advance
This solution depends on a couple of things to work. First, the url and score keys need to be the same, i.e. all lower case and none that are "link." Secondly, the URLs have to be normalized, because they serve as the key for the array. If there are any differences in the URLs, they will show up more than once in the final array.
$merged = array_merge($googleArray, $blekkoArray);
$merged = array_merge($merged, $bingArray);
$combined = array();
foreach ($merged as $key => $value){
$score = (isset($combined[$value['url']]['score'])) ? $value['score'] + $combined[$value['url']]['score'] : $value['score'];
$combined[$value['url']] = $value;
$combined[$value['url']]['score'] = $score;
}
If you don't want to keep the URLs as the key, add this line:
$combined = array_values($combined);
If you want to sort the array by score, you can use usort:
usort($combined, function ($a, $b){
return $b['score'] - $a['score'];
});
print_r($combined);

Categories