Eliminate Partial Strings from Array of Strings PHP - php

I can do this, I'm just wondering if there is a more elegant solution than the 47 hacked lines of code I came up with...
Essentially I have an array (the value is the occurrences of said string);
[Bob] => 2
[Theresa] => 3
[The farm house] => 2
[Bob at the farm house] => 1
I'd like to iterate through the array and eliminate any entries that are sub-strings of others so that the end result would be;
[Theresa] => 3
[Bob at the farm house] => 1
Initially I was looping like (calling this array $baseTags):
foreach($baseTags as $key=>$count){
foreach($baseTags as $k=>$c){
if(stripos($k,$key)){
unset($baseTags[$key]);
}
}
}
I'm assuming I'm looping through each key in the array and if there is the occurrence of that key inside another key to unset it... doesn't seem to be working for me though. Am I missing something obvious?
Thank you in advance.
-H

You're mis-using strpos/stripos. They can return a perfectly valid 0 if the string you're searching for happens to be at the START of the 'haystack' string, e.g. your Bob value. You need to explicitly test for this with
if (stripos($k, $key) !== FALSE) {
unset(...);
}
if strpos/stripos don't find the needle, they return a boolean false, which under PHP's normal weak comparison rules is equal/equivalent to 0. Using the strict comparison operators (===, !==), which compare type AND value, you'll get the proper results.

Don't forget as-well as needing !== false, you need $k != $key so your strings don't match themselves.

You have two problems inside your code-example:
You combine each key with each key, so not only others, but also itself. You would remove all entries so because "Bob" is a substring of "Bob", too.
stripos returns false if not found which can be confused with 0 that stands for found at position 0, which is also equal but not identical to false.
You need to add an additional check to not remove the same key and then fix the check for the "not found" case (Demo):
$baseTags = array(
'Bob' => 2,
'Theresa' => 3,
'The farm house' => 2,
'Bob at the farm house' => 1,
);
foreach ($baseTags as $key => $count)
{
foreach ($baseTags as $k => $c)
{
if ($k === $key)
{
continue;
}
if (false !== stripos($k, $key))
{
unset($baseTags[$key]);
}
}
}
print_r($baseTags);
Output:
Array
(
[Theresa] => 3
[Bob at the farm house] => 1
)

Related

Simplify PHP array with same items

I have this PHP array:
$this->user_list = array( 0 => 'Not paid',1 => 'Not paid', 2 => 'Not paid', 7 => 'Waiting, 15 => 'Waiting', 10 => 'Cancelled' );
How can I simplify this array as the id numbers are different, but some of them have same status?
I tried it like this:
$this->user_list = array( [0,1,2 => 'Not paid'],[7,15 => 'Waiting'],10 => 'Cancelled' );
but it doesn't work as expected.
Basically I want to achieve this:
echo $this->user_list[15] should give me Waiting, echo $this->user_list[10] should give me Cancelled, etc. So this is working in my first array very well, I am just thinking about grouping duplicate names there.
As mentioned by other contributors, there is no native support in the PHP grammar for your intended use case. As clearly stated in the PHP: Arrays documentation:
An array can be created using the array() language construct. It takes any number of comma-separated key => value pairs as arguments.
So basically each element in an array is a key => value pair, which means you cannot associate multiple keys to a single element.
This also explains why your first tentative didn't work:
$this->user_list = array( [0,1,2 => 'Not paid'],[7,15 => 'Waiting'],10 => 'Cancelled' );
If you don't specify a key for an element, PHP uses a progressive index (0, 1, ...). So basically in the example above, the first zero is not actually a key, but a value, and PHP binds it to the key = 0. Maybe it could be easier for you to understand how it works if you print a var_dump or print_r of $this->user_list. You would get something similar to the following structure (NOTE: I have simplified the structure to make it more clear):
[
0 => [
0 => 0
1 => 1
2 => "Not paid"
],
1 => [
0 => 7,
15 => "Waiting"
],
10 => "Cancelled"
]
So how do we resolve this problem? Well... actually there is no need to contort the structure by swapping keys with values as other contributors seem to suggest. Changing the structure might simplify your "data entry" work but might also create big issues in other parts of the program because who knows, maybe accessing the invoice data by "ID" is simply more efficient than by "status" ... or something.
Since PHP does not provide such a feature out of the box, I believe a better solution would be to develop our own function; a good starting point could be the one in the example below.
function explode_array($config, $sep = ',') {
$res = [];
foreach($config as $configKey => $value) {
// split key values
$keys = explode($sep, $configKey);
foreach($keys as $key) {
$res[$key] = $value;
}
}
return $res;
}
$config = [
'0,1,2' => 'Not paid',
'7,15' => 'Waiting',
'10' => 'Cancelled'
];
$myArr = explode_array($config);
print_r($myArr);
The idea is quite simple: since we cannot use an array as key we leverage the next best data type, that is a CSV string. Please note there is no error handling in the above code, so the first thing you may want to do is adding some validation code to the explode_array (or however you wish to name it) function.
you should use like this. if id number is invoice id or something else and other value is there status about it.
$arr = array(
'Not paid' => [0,1,2] ,
'Waiting' => [5,6],
'Cancelled' =>[8]
);
foreach($arr as $key => $val){
foreach($val as $keys => $vals){
echo "invoiceid ".$vals ." status ".$key;
echo"<br>";
}
}
// for only one status you can use like this
foreach($arr['Not paid'] as $key => $val){
echo $val;
echo"<br>";
}
just try to run this and check output.
PHP has no built-in function or structure for handling cases like this. I'd use a simple array value-cloning function to map your duplicates. Simply have one instance of each status, then map the aliases, and then run a function that clones them in. As follows:
// Status list:
$ulist = [ 0 => 'Not paid', 7 => 'Waiting', 10 => 'Cancelled' ];
// Alternative IDs list, mapped to above source IDs:
$aliases = [ 0 => [1,2], 7 => [15] ];
// Function to clone array values:
function clone_values(array &$arr, array $aliases)
{
foreach($aliases as $src => $tgts) {
foreach($tgts as $tgt) {
$arr[$tgt] = $arr[$src];
}
}
ksort($arr); // If the order matters
}
// Let's clone:
clone_values($ulist, $aliases);
This results in the following array:
array(6) {
[0] · string(8) "Not paid"
[1] · string(8) "Not paid"
[2] · string(8) "Not paid"
[7] · string(7) "Waiting"
[10] · string(9) "Cancelled"
[15] · string(7) "Waiting"
}
....which can be accessed as you expect, here $ulist[2] => Not paid, etc. If the use case is as simple as illustrated in the OP, I'd personally just spell it out as is. There's no dramatic complexity to it. However, if you have dozens of aliases, mapping and cloning begins to make sense.
As said in the comments, you can't have multiple keys with one value. The best way is to use the keyword => [ number, number, number...] construction.
//set a result array
$result = [];
//loop the original array
foreach ( $this->user_list as $number => $keyword ){
//if the keyword doesn't exist in the result, create one
if(!isset ( $result [ $keyword ] ) ) $result[ $keyword ] = [];
//add the number to the keyword-array
$result[ $keyword ] [] = $number;
}

php associative array : compare adjacent values

I would like to check many rows of the same 2 input fields in a form. The validation should fail if one is empty and the other is not.
I have created an associative array based on several input fields(e_me_id, e_md_number, e_md_id1,e_md_number1, p_me_id,p_md_number...) in a form.
$pattern='(md_number|me_id)';
foreach($_POST as $field => $value) {
$success = preg_match($pattern, $field);
if ($success) {
$validate += [$field => $value];
}
}
result of validate =(
[e_me_id] => 1
[e_md_number] => 111
[e_me_id2] => 2
[e_md_number2] => 222
[p_me_id] => 10
[p_md_number] => 101010
[f_me_id] => 16
[f_md_number] => 161616
[d_me_id] => 18
[d_md_number] => 181818 )
I need some looping php to check that the first/second are both null or both filled... same for third/forth, fifth/sixth... etc etc.
I tried to use prev($validate) and next($validate) but could not get it to work.
Any ideas or a different approach.
Thanks in advance.
You can do this to do the validation of pairs:
// assuming this is the array generated by your code ...
$tst= array("e_me_id" => 1,"e_md_number" => 111 ,
"e_me_id2" => 2,"e_md_number2" => 222 ,
"p_me_id" => 0,"p_md_number" => 101010 ,
"f_me_id" => 16,"f_md_number" => 161616 ,
"d_me_id" => 0,"d_md_number" => 0 );
// then this will do the validation of pairs:
$keys=array_keys($tst);
for ($i=0;$i<count($tst);$i+=2)
echo "$keys[$i] and "
.$keys[$i+1]
.(empty($tst[$keys[$i]]) == empty($tst[$keys[$i+1]])?'':' DO NOT')
." pass the validation.\n";
You can see a demo here: https://rextester.com/VEMUFJ14979
I changed a few of the numbers to demonstrate the different possible cases.
Using the == operator between the two empty()-tests is equivalent to using the negated xor operator as suggested by #Markus Zeller.
Edit:
A "shorthand-if statement" should look something like this:
$x = (empty($tst[$keys[$i]]) == empty($tst[$keys[$i+1]])?'':$fieldname);
Although to me it is not quite clear what you intend $x and $fieldname to be in your for-loop. This would set $x to '' if case both tested values were empty or both were "filled" and it would set it to $fieldname when only one of them was empty.
Your most recent comment led me to believe that you want to have an overall validation result in $x. In order to get this you will have to count all errors while looping over the pairs. Something like the following should do that:
for ($i=0,$x=0;$i<count($tst);$i+=2){
if (empty($tst[$keys[$i]]) != empty($tst[$keys[$i+1]])) $x++;
// echo $keys[$i].", error count so far: $x\n";
}
echo ("here ".($x?'validation error!':'OK'));
See the demo here: https://rextester.com/XTK88037

Ignore # array keys in php foreach loop

I am trying to loop over certain array keys in drupal, but this is more of a generic php array question.
The array looks something like this...
$form['items'] = array(
#title => 'hello',
0 => array(
#subtitle => 'hello2';
),
1 => array(
#subtitle => 'hello2';
),
#prefix => '<div>hello</div>',
);
As you can see, the keys are a mix of numeric keys and #meta keys.
I am using this...
foreach($form['items'] as $x) {
unset($form['items'][$x]['column1']);
}
But i only want to target the numeric keys, I have tried is_numeric but it returned false.
Can someone tell me how to ignore the other keys? (Ignore #title and #prefix etc)
You want to check the keys, but you are using the value in your foreach. Do the following:
foreach($form['items'] as $key => $value) {
if (is_numeric($key))
unset($form['items'][$key]);
}
Hope I was helpful
Use is_int() rather than is_numberic()
foreach ($input_array as $key => $val) {
if (is_int($key)) {
// do stuff
}
}
Important to note that is_int only works on things that are type integer, meaning string representations are not allowed.

How can I replace a specific key's value in an array in php?

I have a an array that has 3 values. After the user pushes the submit button I want it to replace the the value of a key that I specify with another value.
If I have an array with the values (0 => A, 1 => B, 2 => C), and the function is run, the resulting array should be (0 => A, 1 => X, 2 => C), if for example the parameter for the function tells it to the replace the 2nd spot in the array with a new value.
How can I replace a specific key's value in an array in php?
If you know the key, you can do:
$array[$key] = $newVal;
If you don't, you can do:
$pos = array_search($valToReplace, $array);
if ($pos !== FALSE)
{
$array[$pos] = $newVal;
}
Note that if $valToReplace is found in $array more than once, the first matching key is returned. More about array_search.
In case you want to have an inline solution you can use array_replace or array_replay_recrusive depending on which suits you best.
$replaced_arr = array_replace([
'key' => 'old_value',
0 => 'another_untouched_value'
],[
'key' => 'new_value'
]);
It would be best if your array is key/value pair

How to search an array key by matching a string in it's value

I'm trying to find the key number in a array matching a string.
I tried array_search in this way
$key = array_search("foo", $array);
echo $array[$key];
but that prints $array[0]
Is there another way to do this?
Thanks :)
If the key is not found, array_search returns false. You have to check for that (line 3 in my example below)
$array = array(0 => 'blue', 1 => 'red', 2 => 'green', 3 => 'red');
$key = array_search("green", $array); //the $key will be "2"
if ($key !== false) {
echo $array[$key];
}
Otherwise, your code seems to do what you need. If there is a problem, please, post more code.
I'm not exactly matching the whole string, just one part, will array_search still work?
btw i made a loop through the array with for each that do a preg_match until it finds the string then breaks the loop and store the key in a array

Categories