Check if array element already partially exists inside of the array - php

I have an array that looks like this:
[Amsterdam, Elderly people, Thousand students, Sixteen thousand students, Clean houses]
As you can see, there's an entry "thousand students" and an entry "sixteen thousand students". Is there a way that would let me filter out thousand students (and delete this entry), since it already partially exists?
However, just unsetting the element by hand won't work. The array is the result of an API, which means I won't know if there are partial duplicates or not.
thanks.
Edit: Expected result:
[Amsterdam, Elderly people, Sixteen thousand students, Clean houses]

So I'm trying to work out a slicker way without two loops, but this will do it:
foreach($array as $k => $a) {
foreach($array as $b) {
if(strtolower($a) !== strtolower($b) &&
(strpos(strtolower($b), strtolower($a)) !== false)) {
unset($array[$k]);
}
}
}
Loop the array and compare each value in lower case to each other value in lower case
If they are not equal and one is found within the other, use the key to remove the one found in the other
Maybe a little shorter:
foreach(array_map('strtolower', $array) as $k => $a) {
foreach(array_map('strtolower', $array) as $b) {
if($a !== $b && (strpos($b, $a) !== false)) {
unset($array[$k]);
}
}
}

Try this:
<?php
function custom_filter( $data ) {
$data_lc = array_map(function($value){
return strtolower($value);
}, $data);
foreach ($data_lc as $keyA => $valueA) {
foreach ($data_lc as $keyB => $valueB) {
if ( $keyA === $keyB ) {
continue;
}
if ( false !== strpos($valueA, $valueB) ) {
if ( strlen($valueA) <= strlen($valueB) ) {
unset($data[$keyA]);
} else {
unset($data[$keyB]);
}
}
}
}
return $data;
}
$array = ['Amsterdam', 'Elderly people', 'Thousand students', 'Sixteen thousand students', 'Clean houses'];
print_r( custom_filter( $array ) );

This should do the trick, however it'll only look for matches at the word level, and it's case sensitive.
<?php
$wordsList = [
'Amsterdam', 'Elderly people', 'Thousand students',
'Sixteen thousand students', 'Clean houses',
];
$lookup = array();
foreach ($wordsList as $k => $words) {
$phrase = '';
foreach (preg_split('`\s+`', $words, -1, PREG_SPLIT_NO_EMPTY) as $word) {
$phrase .= $word;
if (in_array($phrase, $words)) {
unset($wordsList[$k]);
break;
}
}
}

Related

How to get the values only if all the keys have matched?

I want to make a method that returns keys and values. But only if the keys include the following string "_1" and "__last".
If only one matches then exit the function, only if the two string are included in the key, return the key with the value for a weather.
$infoList = array("_key_1"=>array("time"=>9, "day"=>"Tuesday", "weather"=>"sunny",
"humidity"=>"80%"),
"_key_2"=>array("time"=>5, "day"=>"Tuesday", "weather"=>"cloudy"),
"_key__last"=>array("time"=>3, "day"=>"Sunday", "weather"=>"rainy"))
public function getData() {
$list = array();
foreach($infoList as $key){
if(preg_match('/(_key)_(_1)/', $key) && preg_match('/(_key)_(__last)/', $key) == TRUE){
$list[$key] = $list[$key]["weather"]
}
}
return $list
}
You are making your life so much more difficult that it need be, use str_contains() its easier than building complex REGEX's and getting very confused by the look of it :)
I also fixed a number of other mistakes, such as the foreach that was not going to work, so check all the code.
It is also better to pass data to a function/method otherwise you get into scoping issues!
$infoList = array("_key_1"=>array("time"=>9, "day"=>"Tuesday", "weather"=>"sunny", "humidity"=>"80%"),
"_key_2"=>array("time"=>5, "day"=>"Tuesday", "weather"=>"cloudy"),
"_key__last"=>array("time"=>3, "day"=>"Sunday", "weather"=>"rainy"));
function getData(Array $infoList) {
$list = [];
$found = 0;
foreach($infoList as $key => $val) {
if( str_contains($key, '_1') || str_contains($key, '__last') ) {
$list[$key] = $val["weather"];
$found++;
}
}
if ( $found >= 2 ) {
return $list;
} else {
return false;
}
}
$res = getData($infoList);
if ( $res !== false ){
print_r($res);
} else {
echo 'Not Found';
}
RESULTS
Array
(
[_key_1] => sunny
[_key__last] => rainy
)
If you want to stick with RegEx, you can use positive lookaheads, the same way you check for passwords characters :
<?php
$pattern = '/^(?=.*_1)(?=.*_last).*$/';
$shouldMatch = [
'_1_last',
'foo_1bar_lasthello',
'_last_1',
'foo_lastbar_1hello'
];
echo 'next ones should match : ' . PHP_EOL;
foreach ($shouldMatch as $item)
{
if (preg_match($pattern, $item))
echo $item . PHP_EOL;
}
$shouldNOTMatch = [
'_2_first',
'bar_lasthello',
'foo_las_1hello'
];
echo 'next ones should NOT match : ' . PHP_EOL;
foreach ($shouldNOTMatch as $item)
{
// v------------ check
if (!preg_match($pattern, $item))
echo $item . PHP_EOL;
}
Output :
next ones should match :
_1_last
foo_1bar_lasthello
_last_1
foo_lastbar_1hello
next ones should NOT match :
_2_first
bar_lasthello
foo_las_1hello

How to simplify and optimize the function?

A now write this function for my script. It works well type but a little slows down. Consider the function and if you have options for optimally ask me to help.
Here is my code:
function izada($array) {
foreach ($array as $key => $value) {
if(substr_count($value, "ӣ") == 2) {
$result[] = str_replace("ӣ ", "ӣ, ", $value);
}
if(mb_substr($value, -1) !== "ӣ") {
unset($array[$key]);
}
if(substr_count($value, "ӣ") == 2) {
unset($array[$key]);
}
$array = array_filter(array_unique(array_merge($array, $result)));
}
foreach ($array as $key => $value) {
if(substr_count($value, "ӣ") > 2 || substr_count($value, "ӣ") < 1) {
unset($array[$key]);
}
}
return $array;
}
Input and function call:
$array = array (
"забони тоҷикӣ",
"хуҷандӣ бӯстонӣ",
"Тоҷикистон Ватанам",
"Ғафуровӣ Мичуринӣ Савхозӣ",
"Конверторӣ хуруфҳо"
);
$array = izada($array);
echo"<pre>";
print_r($array);
echo"</pre>";
Result must be:
Array (
[0] => забони тоҷикӣ
[1] => хуҷандӣ, бӯстонӣ
)
Jakub's answer is not optimized and is potentially incorrect according to your posted method.
It allows the possibility of a value with 2 ӣ's but not ending with ӣ to qualify. (If this is acceptable, then you should clarify your question requirements.)
It calls substr_count() 1 to 3 times per iteration (depending on conditional outcomes). The important thing to consider for efficiency is minimizing function calls.
This is a more accurate / efficient process:
Input:
$array=[
"забони тоҷикӣ",
"хуҷандӣ бӯстонӣ",
"Тоҷикистон Ватанам",
"Ғафуровӣ Мичуринӣ Савхозӣ",
"Конверторӣ хуруфҳо"
];
Method: (Demo)
foreach($array as $v){
if(mb_substr($v,-1)=="ӣ"){ // require last char to be ӣ
if(($count=substr_count($v,"ӣ"))==1){
$result[]=$v; // do not replace if only 1 ӣ
}elseif($count==2){
$result[]=str_replace("ӣ ","ӣ, ",$v); // replace qualifying ӣ's if 2 ӣ's
}
}
}
var_export($result);
Output:
array (
0 => 'забони тоҷикӣ',
1 => 'хуҷандӣ, бӯстонӣ',
)
Notice that my method first requires the final character to be ӣ, this offers the quickest return without declaring/overwriting $count for non-qualifying values.
$count is used to cache the result of substr_count() for each iteration. By doing this, the iteration only needs to make the function call once -- improving efficiency.
All the array_merge and array_unique are taking up unnecessary resources. Instead of trying to alter the original array, why not create an output array and fill it with the data you want?
There are also several redundant conditions - you are checking for the same thing several times. From what I understood, this is what you want:
Return all strings where ӣ is present once or twice, either at the end or twice anywhere. If it is present twice, add a coma.
So you could simplify it like
function izada($array) {
$ret = [];
foreach($array as $string){
if (substr_count($string, "ӣ") >= 1 && substr_count($string, "ӣ") <= 2) {
if(substr_count($string, "ӣ") == 2) {
$ret[] = str_replace("ӣ ", "ӣ, ",$string);
}
else if (mb_substr($string, -1) == "ӣ") {
$ret[] = $string;
}
}
}
return $ret;
}

How can I replace a string inside a multidimensional array?

How can I replace a string/word that's inside a multidimensional array with a new value? I don't have its key, just know the haystack and the needle.
Say I have a multidimensional array, $submenu_arr, (don't know how many dimensions).
I want to find a value inside one of these arrays and replace it with a new value.
Actually for a translation.
Like:
recursive_arr_translation('Article', $submenu_arr, 'Artigo');//"Artigo" is a Portuguese word for "Article".
I've tried this, but not working:
function in_array_r($needle, $haystack, $new_value) {
$found = false;
foreach ($haystack as $key=>$value) {
if ($value === $needle) {
$found = true;
$haystack[$key] = $new_value;
return true;
} elseif (is_array($value)) {
$found = in_array_r($needle, $haystack[$key], $new_value);
if($found) {
return true;
}
}
}
return $found;
}
in_array_r('Article', $submenu, 'Artigo');
in_array_r('Location', $submenu, 'Localização');
EDIT: Is working, but somehow, I don't get it working, I'm trying to translate a WordPress submenu word.
You can use array_walk_recursive as suggested in the comments, and pass your original array as reference, allowing us to edit it.
<?php
$a = array("Giraffe", "Monkey", "Elephant", "Snake", 5, "other" => array("apple", "orange"));
array_walk_recursive($a, function(&$a) {
if($a == "apple") {
$a = "Banana";
}
});
echo print_r($a, true);
https://eval.in/198978
So, now we have the basic logic, let's create a function with 3 parameters.
function replace_in_array($find, $replace, &$array) {
array_walk_recursive($array, function(&$array) use($find, $replace) {
if($array == $find) {
$array= $replace;
}
});
return $array;
}
$a = array("Giraffe", "Monkey", "Elephant", "Snake", 5, "other" => array("apple", "orange"));
echo print_r( replace_in_array("apple", "banana", $a), true);
https://eval.in/198989

search words in two arrays

I am trying to search two arrays and return the index of matching words that match in array 1 from array 2. following are the arrays:
$array1 = array('hello how are you', 'hello I am fine');
$array2 = array('hello','how');
I am trying the following code and it return 0,1 which is fine. But i only want to return 0. I want it to return only where both words are present in the array.
foreach ($array1 as $reference => $array) {
foreach($array2 as $key => $word) {
if(strpos($array, $word) !== false) {
echo $reference, PHP_EOL;
break;
}
}
}
You need to keep track of each entity from $array2 being checked against each entity from $array1 then compare after the inner loop to decide whether all elements from $array2 are present in $array1. Here's an example:
foreach($array1 as $reference => $array) {
$contains = 0;
foreach($array2 as $key => $word) {
if(strpos($array, $word) !== false) {
$contains++;
} else {
// for performance reasons, e.g. if you have a large array,
// you should break the loop here if the word isn't in the
// original array
break;
}
}
if($contains == count($array2)) {
// $array contains all words from $array2
echo $reference . PHP_EOL;
} else {
// $array doesn't contain all the words
}
}

generic function for extracting values from an array with one particular key in PHP

Is it possible in PHP to extract values from an array with a particular key path and return an array of those values? I'll explain with an example:
$user =
array (
array(
'id' => 1,
'email' =>'asd#example.com',
'project' => array ('project_id' => 222, 'project_name' => 'design')
),
array(
'id' => 2,
'email' =>'asd2#example.com',
'project' => array ('project_id' => 333, 'project_name' => 'design')
)
);
/** I have to write a function something like: */
$projectIds = extractValuesWithKey($user, array('project', 'project_id'));
print_r($projectIds);
Output:
Array(
[0] => 222,
[1] => 333
)
I would have gone for a different approach (not that there's anything wrong with the array-function-based answers) by using a recursive iterator to flatten the array which makes the key-path comparison fairly simple.
function extractValuesWithKey($array, $keys) {
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($array));
$keys_count = count($keys);
// No point going deeper than we have to!
$iterator->setMaxDepth($keys_count);
$result = array();
foreach ($iterator as $value) {
// Skip any level that can never match our keys
if ($iterator->getDepth() !== $keys_count) {
continue;
}
// Build key path to current item for comparison
$key_path = array();
for ($depth = 1; $depth <= $keys_count; $depth++) {
$key_path[] = $iterator->getSubIterator($depth)->key();
}
// If key paths match, add to results
if ($key_path === $keys) {
$result[] = $value;
}
}
return $result;
}
To make the whole thing more useful, you could even wrap the code into a custom FilterIterator rather than a basic function, but I guess that's probably a different question entirely.
Well, that's easier than you think.
function extractValuesWithKey($array, $parts) {
$return = array();
$rawParts = $parts;
foreach ($array as $value) {
$tmp = $value;
$found = true;
foreach ($parts as $key) {
if (!is_array($tmp) || !isset($tmp[$key])) {
$found = false;
continue;
} else {
$tmp = $tmp[$key];
}
}
if ($found) {
$return[] = $tmp;
}
}
return $return;
}
If the 'key path' isn't dynamic, you can do a one-liner with array_map:
$projectIds = array_map(function($arr) { return $arr['project']['project_id']; }, $user);
Alternatively, for dynamic paths:
function extractValuesWithKey($users, $path) {
return array_map(function($array) use ($path) {
array_walk($path, function($key) use (&$array) { $array = $array[$key]; });
return $array;
}, $users);
}
The closures/anonymous functions only work with PHP 5.3+, and I've no idea how this would compare performance-wise to a double foreach loop. Note also that there's no error checking to ensure that the path exists.
I also used a similiar function in one of my projects, maybe you find this useful:
function extractValuesWithKey($data, $path) {
if(!count($path)) return false;
$currentPathKey = $path[0];
if(isset($data[$currentPathKey])) {
$value = $data[$currentPathKey];
return is_array($value) ? extractValuesWithKey($value, array_slice($path, 1)) : $value;
}
else {
$tmp = array();
foreach($data as $key => $value) {
if(is_array($value)) $tmp[] = extractValuesWithKey($value, $path);
}
return $tmp;
}
}

Categories