Related
I have a string $raw="aabbcdfghmnejaachto" and an array $word_array=array('cat','rat','goat','total','egg').
My program needs to check whether it is possible to make the words in the array with letters from the string. There is one extra condition; if the word contains a letter occurring more than once, that letter must occur at least the same number of times in the string.
E.g. egg. There are two g's. If the string $raw doesn't contain two g's, then it's not possible to make this word.
This is my expected result:
Array([cat]=>'Yes',[rat]=>'No',[goat]=>'Yes',[total]=>'No',[egg]=>'No')
I tried the following, but it doesn't output the expected result:
$res=array();
$raw="aabbcdfghmnejaachto";
$word_array=array('cat','rat','goat','total','egg');
$raw_array= str_split($raw);
foreach($word_array as $word=>$value)
{
$word_value= str_split($value);
foreach($word_value as $w=>$w_value)
{
foreach($raw_array as $raw=>$raw_value)
{
if(strcmp($w_value,$raw_value)==0)
{
$res[$value]='Yes';
}
else
{
$res[$value]='No';
}
}
}
}
print_r($res);
EDIT: The code, as originally posted, was missing the letter e from the string $raw so the egg example would actually return No. I have updated the Question and all the Answers to reflect this. - robinCTS
You must loop through each word/element in the $words array, then loop again through each character of each word.
Upon each iteration of the outer loop, set the default result value to Yes.
Then you must iterate each unique character of the current word. (array_count_values())
Check if the number of occurrences of the current character in the word is greater than the number of occurrences of the current character in the string of letters.
*As a matter of performance optimization, array_count_values() is used on the inner loop to avoid any unnecessary iterations of duplicate letters in $word. The $count variable saves having to make two substr_count() calls in the if statement.
Code: (Demo)
$string = "aabbcdfghmnejaachto";
$words = array('cat','rat','goat','total','egg');
foreach ($words as $word) { // iterate each word
$result[$word]='Yes'; // set default result value
foreach (array_count_values(str_split($word)) as $char=>$count) { // iterate each unique letter in word
if ($count > substr_count($string, $char)) { // compare current char's count vs same char's count in $string
$result[$word]='No'; // if more of the character in word than available in $string, set No
break; // make early exit from inner loop, to avoid unnecessary iterations
}
}
}
var_export($result);
This is the output :
array (
'cat' => 'Yes',
'rat' => 'No',
'goat' => 'Yes',
'total' => 'No',
'egg' => 'No',
)
BIG THANKYOU to mickmackusa for hijacking significantly enhancing this answer.
Your problem is you are not counting the number of times each character occurs in the $raw array, you are just checking each character in each of the words to see if that character exists in $raw. Unless you put in some form of counting, or else make a copy of $raw for each word and remove letters as they are used, you are not going to be able to do this.
I have counted occurrences of characters in string and compare that number of occurrence! You can find this answer working!!!
$res=array();
$raw="aabbcdfghmnejaachto"; //tgrel -- to make all yes
$res=array();
$word_array=array('cat','rat','goat','total','egg');
$raw_array= str_split($raw);
$count_raw = array_count_values($raw_array);
foreach($word_array as $value)
{
$word_value= str_split($value);
$newArray = array_count_values($word_value);
$res[$value]='yes';
foreach($newArray as $char=>$number){
if(!isset($count_raw[$char]) || $count_raw[$char]<$number){
$res[$value]='No';
break;
}
}
}
print_r($res);
Your error here is obvious, that you decided whether a value a word is accepted or not on individual tests of characters, while it should be based on the all the letter of the word , you don't need to precise both the key and value of an array if you need only its value
as in
foreach($word_array as $value)
then I've found that the use of the function in_array(), make the code much clearer
$res=array();
$raw="aabbcdfghmnejaachto";
$res=array();
$word_array=array('cat','rat','goat','total','egg');
$raw_array= str_split($raw);
foreach($word_array as $value)
{
$word_value= str_split($value);
$res[$value]='yes';
foreach($word_value as $w_value)
{
if (!in_array($w_value,$raw_array))
$res[$value]='No';
}
}
print_r($res);
Lets try to make it w/o loops, but with closures:
$raw = "aabbcdfghmnejaachto";
$word_array = ['cat', 'rat', 'goat', 'total', 'egg'];
$result = [];
$map = count_chars($raw, 1);
array_walk(
$word_array,
function ($word) use ($map, &$result) {
$result[$word] = !array_udiff_assoc(
count_chars($word, 1), $map, function ($i, $j) { return $i > $j; }
) ? 'Yes' : 'No';
}
);
We are building a map of symbols, used in original string with count_chars($raw, 1), so it will look like this.
$map:
[
97 => 4, // "97" is a code for "a"; and "4" - occurrence number.
98 => 2,
...
]
array_walk goes through words and adds each of them in a final $result with a Yes or No values that come from a comparison with a map, that was built for a word.
array_udiff_assoc compares two maps, throwing away those elements that have the same key and values bigger for an original map (comparing with a map for a word). Also array_udiff_assoc() returns an array containing all the values from array1 that are not present in any of the other arguments, so the final step is a negation operation preceding array_udiff_assoc.
Demo
Try this
$res=array();
$word_array=array('cat','rat','goat','total','egg');
$raw="aabbcrdfghmnejaachtol";
foreach($word_array as $word=>$value)
{
$raw_array= str_split($raw);
$res[$value]='Yes';
$word_value= str_split($value);
foreach($word_value as $w=>$w_value)
{
if(!in_array($w_value,$raw_array))
{
$res[$value]='No';
}
else
{
unset($raw_array[array_search($w_value, $raw_array)]);
}
}
}
This will not allow character again, if it is used once Like "total".
We can check to see if each letter from each word is within the letters given, and pluck found letters out as we go.
The function below short circuits if a letter is not found.
<?php
function can_form_word_from_letters($word, $letters) {
$letters = str_split($letters);
$word_letters = str_split($word);
foreach($word_letters as $letter) {
$key = array_search($letter, $letters);
if($key === false) return;
unset($letters[$key]); // Letter found, now remove it from letters.
}
return true;
}
$letters = "aabbcdfghmnejaachto";
$words = array('cat','rat','goat','total','egg');
foreach($words as $word) {
$result[$word] = can_form_word_from_letters($word, $letters) ? 'Yes' : 'No';
}
var_dump($result);
Output:
array (size=5)
'cat' => string 'Yes' (length=3)
'rat' => string 'No' (length=2)
'goat' => string 'Yes' (length=3)
'total' => string 'No' (length=2)
'egg' => string 'No' (length=2)
I have an array containing some words and I want to remove the words that contain either a . (fullstop) or ; (semicolon) or some other symbols. I have read the solution on [ Remove item from array if item value contains searched string character ] but this doesn't seem to answer my problem.
What can I add to this code to remove also the words containing the other symbols other than semicolon?
function myFilter($string) {
return strpos($string, ';') === false;
}
$newArray = array_filter($array, 'myFilter');
Thanks
Use preg_match function:
function myFilter($string) {
return !preg_match("/[,.]/", $string);
}
[,.] - character class which can be extended with any other symbols
// $array is your initial array
$newArray = array();
foreach ($array as $item){
if ((strpos($item, ';') > 0)||(strpos($item, '.') > 0))
continue;
$newArray[] = $item;
}
// Words with ; or . should be filtered out in newArray
print_r($newArray);
So I'm trying to decode an anagram into words from my dictionary file. But my recursive function isn't behaving like I'm expecting.
The thoughts about the code is to eliminate letters as they are used on words and output me the string it came up with.
<?php
function anagram($string, $wordlist)
{
if(empty($string))
return;
foreach($wordlist as $line)
{
$line = $org = trim($line);
$line = str_split($line);
sort($line);
foreach($line as $key => $value)
{
if($value != $string[$key])
{
continue 2;
}
}
echo $org . anagram(array_slice($string, count($line)), $wordlist);
}
echo PHP_EOL;
}
$string = "iamaweakishspeller";
$string = str_split($string);
sort($string);
$file = file('wordlist');
anagram($string, $file);
This is my result for now, it looks awful, but I'm having some issues with the code - it's going into an indefinite loop with the same roughly 200 words from the word list.
Can someone take an extra peak at this?
Situation
You have a dictionary(file) and an anagram which contains one or multiple words. The anagram doesn't contain any punctuation or letter case of the original word(s).
Now you want to find all true solutions where you use up all characters of the anagram and decode it into word(s) from the dictionary.
Note: There is a chance that you find multiple solutions and you will never know which one the original text was and in which order the words were, since the characters of multiple words are mixed in the anagram and you don't have punctuation or the case of the letters in it.
Your code
The problem in your current code is exactly that you have multiple words mixed together. If you sort them now and you want to search them in the dictionary you won't be able to find them, since the characters of multiple words are mixed. Example:
anagram = "oatdgc" //"cat" + "dog"
wordList = ["cat", "dog"]
wordListSorted = ["act", "dgo"]
anagramSorted = acdgot
↓↓↓
WordListSorted[0] → cat ✗ no match
WordListSorted[1] → dog ✗ no match
Solution
First I will explain in theory how we construct all possible true solutions and then I explain how every part in the code works.
Theory
So to start we have an anagram and a dictionary. Now we first filter the dictionary by the anagram and only keep the words, which can be constructed by the anagram.
Then we go through all words and for each word we add it to a possible solution, remove it from the anagram, filter the dictionary by the new anagram and call the function with the new values recursively.
We do this until either the anagram is empty and we found a true solution, which we add to our solution collection, or there are no words remaining and it is not a possible solution.
Code
We have two helper functions array_diff_once() and preSelectWords() in our code.
array_diff_once() is pretty much the same as the built-in array_diff() function, except that it only removes values once and not all occurrences. Otherwise there isn't much to explain. It simply loops through the second array and removes the values once in the first array, which then gets returned.
function array_diff_once($arrayOne, $arrayTwo){
foreach($arrayTwo as $v) {
if(($key = array_search($v, $arrayOne)) !== FALSE)
array_splice($arrayOne, $key, 1);
}
return $arrayOne;
}
preSelectWords() takes an anagram and a word list as argument. It simply checks with the help of array_diff_once(), which words of the word list can be constructed with the given anagram. Then it returns all possible words from the word list, which can be constructed with the anagram.
function preSelectWords($anagram, $wordList){
$tmp = [];
foreach($wordList as $word){
if(!array_diff_once(str_split(strtolower($word)), $anagram))
$tmp[] = $word;
}
return $tmp;
}
Now to the main function decodeAnagram(). We pass the anagram and a word list, which we first filter with preSelectWords(), as arguments to the function.
In the function itself we basically just loop through the words and for each word we remove it from the anagram, filter the word list by the new anagram and add the word to a possible solution and call the function recursively.
We do this until either the anagram is empty and we found a true solution, which we add to our solution array, or there are no words left in the list and with that no possible solution.
function decodeAnagram($anagram, $wordList, $solution, &$solutions = []){
if(empty($anagram) && sort($solution) && !isset($solutions[$key = implode($solution)])){
$solutions[$key] = $solution;
return;
}
foreach($wordList as $word)
decodeAnagram(array_diff_once($anagram, str_split(strtolower($word))), preSelectWords(array_diff_once($anagram, str_split(strtolower($word))), $wordList), array_merge($solution, [$word]), $solutions);
}
Code
<?php
function decodeAnagram($anagram, $wordList, $solution, &$solutions = []){
if(empty($anagram) && sort($solution) && !isset($solutions[$key = implode($solution)])){
$solutions[$key] = $solution;
return;
}
foreach($wordList as $word)
decodeAnagram(array_diff_once($anagram, str_split(strtolower($word))), preSelectWords(array_diff_once($anagram, str_split(strtolower($word))), $wordList), array_merge($solution, [$word]), $solutions);
}
function preSelectWords($anagram, $wordList){
$tmp = [];
foreach($wordList as $word){
if(!array_diff_once(str_split(strtolower($word)), $anagram))
$tmp[] = $word;
}
return $tmp;
}
function array_diff_once($arrayOne, $arrayTwo){
foreach($arrayTwo as $v) {
if(($key = array_search($v, $arrayOne)) !== FALSE)
array_splice($arrayOne, $key, 1);
}
return $arrayOne;
}
$solutions = [];
$anagram = "aaaeeehiikllmprssw";
$wordList = ["I", "am", "a", "weakish", "speller", "William", "Shakespeare", "other", "words", "as", "well"];
//↑ file("wordlist", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES)
decodeAnagram(str_split(strtolower($anagram)), preSelectWords(str_split(strtolower($anagram)), $wordList), [], $solutions);
print_r($solutions);
?>
Output
Array
(
[Iaamspellerweakish] => Array
(
[0] => I
[1] => a
[2] => am
[3] => speller
[4] => weakish
)
[ShakespeareWilliam] => Array
(
[0] => Shakespeare
[1] => William
)
)
(Ignore the keys here, since those are the identifiers of the solutions)
$find=array('or','and','not');
$text=array('jasvjasvor','asmasnand','tekjbdkcjbdsnot');
I have to check if text array contains any of the elements find has. I'm able to do this for single text but don't know how to do it for all texts
$counter=0;
foreach($find as $txt){
if (strstr($text[0], $txt)) {
$counter++;
}
If i use this technique i'll have to run foreach number of times. Is there any other way to do this?
NOTE if array value contains or,and ,not not the whole word match
http://codepad.viper-7.com/VKBMtP
Input
$find=array('or','and','not');
$text=array('jasvjasvor','asmasn','tekjbdkcjbdsnot');
// array values "jasvjasvor" and "tekjbdkcjbdsnot" contains words `or,not`
Output
2 -> as two words from find array are contained in text array values
Use array_intersect():
if (count(array_intersect($find, $text)) >= 1) {
// both arrays have at least one common element
}
Demo.
UPDATE: If you're trying to find how many elements in $text array contain any of the values (partial match or whole-word match) in $find array, you can use the following solution:
$counter = 0;
foreach($find as $needle) {
foreach ($text as $haystack) {
if(strpos($haystack, $needle) !== false) $counter++;
}
}
echo $counter; // => 2
Demo.
$counter=0;
foreach($find as $txt){
foreach($txt as $value){
if (strstr($value, $txt)) {
$counter++;
}
}
I have a page with a form where I post all my checkboxes into one array in my database.
The values in my database looks like this: "0,12,0,15,58,0,16".
Now I'm listing these numbers and everything works fine, but I don't want the zero values to be listed on my page, how am I able to search through the array and NOT list the zero values ?
I'm exploding the array and using a for each loop to display the values at the moment.
The proper thing to do is to insert a WHERE statement into your database query:
SELECT * FROM table WHERE value != 0
However, if you are limited to PHP just use the below code :)
foreach($values AS $key => $value) {
//Skip the value if it is 0
if($value == 0) {
continue;
}
//do something with the other values
}
In order to clean an array of elements, you can use the array_filter method.
In order to clean up of zeros, you should do the following:
function is_non_zero($value)
{
return $value != 0;
}
$filtered_data = array_filter($data, 'is_non_zero');
This way if you need to iterate multiple times the array, the zeros will already be deleted from them.
you can use array_filter for this. You can also specify a callback function in this function if you want to remove items on custom criteria.
Maybe try:
$out = array_filter(explode(',', $string), function ($v) { return ($v != 0); });
There are a LOT of ways to do this, as is obvious from the answers above.
While this is not the best method, the logic of this might be easier for phpnewbies to understand than some of the above methods. This method could also be used if you need to keep your original values for use in a later process.
$nums = '0,12,0,15,58,0,16';
$list = explode(',',$nums);
$newList = array();
foreach ($list as $key => $value) {
//
// if value does not equal zero
//
if ( $value != '0' ) {
//
// add to newList array
//
$newList[] = $value;
}
}
echo '<pre>';
print_r( $newList );
echo '</pre>';
However, my vote for the best answer goes to #Lumbendil above.
$String = '0,12,0,15,58,0,16';
$String = str_replace('0', '',$String); // Remove 0 values
$Array = explode(',', $String);
foreach ($Array AS $Values) {
echo $Values."<br>";
}
Explained:
You have your checkbox, lets say the values have been converted into a string. using str_replace we have removed all 0 values from your string. We have then created an array by using explode, and using the foreach loop. We are echoing out all the values of th array minux the 0 values.
Oneliner:
$string = '0,12,0,15,58,0,16';
echo preg_replace(array('/^0,|,0,|,0$/', '/^,|,$/'), array(',', ''), $string); // output 12,15,58,16