PHP combinations using all words each time [duplicate] - php

This question already has answers here:
Combinations Of String While Maintaining Order Of Words
(3 answers)
Closed 8 years ago.
This is my first question at this site, so I hope that I will be specific enough with this.
I need to transform a text string into several array with all different combinations of the 'words' and 'word phrases' in the text string.
So string would be like:
"Football match France 2013"
From this I want the following array:
array(
0 => array(
'Football',
'match',
'France',
'2013'
),
1 => array(
'Football',
'match',
'France 2013'
),
2 => array(
'Football',
'match France',
'2013'
),
3 => array(
'Football',
'match France 2013'
),
4 => array(
'Football match',
'France',
'2013'
),
5 => array(
'Football match',
'France 2013',
),
6 => array(
'Football match France',
'2013'
),
7 => array(
'Football match France 2013',
),
)
So the restriction that a each result string string may consist of 1 to n consecutive words and that in total each sub array should contain each word one time.

Here is some code that works.
<?php
$str = 'Football match France 2013'; // Initialize sentence
$words = explode(" ",$str); // Break sentence into words
$p = array(array(array_shift($words))); // Load first word into permutation that has nothing to connect to
foreach($words as $word) { // for each remaining word
$a = $p; // copy existing permutation for not-connected set
$b = $p; // copy existing permutation for connected set
$s = count($p); // cache number of items in permutation
$p = array(); // reset permutation (attempt to force garbage collection before adding words)
for($i=0;$i<$s;$i++) { // loop through each item
$a[$i][] = $word; // add word (not-connected)
$b[$i][count($b[$i])-1] .= " ".$word; // add word (connected)
}
$p = array_merge($a,$b); // create permutation result by joining connected and not-connected sets
}
// Dump the array
print_r($p);
?>

Related

Create dynamic sub array from a big one [duplicate]

This question already has answers here:
PHP: How to split array into 2 parts?
(6 answers)
Closed 10 months ago.
I have an array, made by using explode and the delimiter #. I'm trying without success to create from this big array in PHP subarrays (the number of subarrays is not fixed and could vary). The goal is to make every 10 # a new sub array and store in all the values between 0 - 9, 10 - 19, etc...
Here my big array where the PHP has to work on : the ( ) are just comment to be more clear, in the code there is only #
#ACT/A (line 1)
#XXX (2)
#2 (3)
#51,6844 (4)
#50,7000 (5)
#101,40 (6)
#-1,97 (7)
#-1,91 (8)
#-0,61 (9)
#3,34 (10)
#ACT/B (11
#X
#4
#68,86750
#63,2700
#253,08
#-22,39
#-8,13
#-0,41
#8,27 (line 20)
#ACT/C
#X
#15
#10,33132
#4,18
#62,70
#-92,27
#-59,54
#0,00
#2,03
My PHP code (which is not working) :
$start = 1;
$equities = explode("#", $allEquities); // split the big array here
//$howManyEquities is a number between 1 and X, only int, not float)
while($start <= $howManyEquities) // doing the loop for each equities counted (could vary from 1 to x)
{
$array[$howManyEquities] = $equities[0]; // trying to store in a array the result (line 1 from the example above, and then line 11, etc...)
$equities = $equities[$start * 10]; // trying to prepare next loop, start (1) * 10 = 11 to catch next time result from line 11
$start++;
}
To sum up, I'm probably very not clear and I apologize. Here an example of the dynamic array I want from the code (I tried foreach loop but didn't seem to work) :
BigArray (the number of key inside vary according to the number of equity) = (
subArray1 = (ACT/A, XXX, 2, 51,6844, etc from line 1 to 10)
subArray2 = (ACT/B, X, 68,86750, etc from line 11 to 20)
subArray3 = (ACT/C, etc)
subArrayX = (ACT/X, etc)
It could be resumed by every ten first values inside a first array, the next ten in another array, and so on until we cover all the big array (that's why I tried $start * 10 in my code). I have to precise that if $howManyEquities = 7 by example, there will be 70 #, if = 5 there will be 50 # and so on.
EDIT : Solution thanks to #user3783243
while($start <= $howManyEquities)
{
$newArray = array_chunk($equities, 10);
$start++;
}
Don't hesitate if you need more information, thanks for reading and enjoy week-end !
Respectfully
As #user3783243 said, array_chunk does the job.
Source string:
$string = '#ACT/A (line 1)
#XXX (2)
#2 (3)
#51,6844 (4)
#50,7000 (5)
#101,40 (6)
#-1,97 (7)
#-1,91 (8)
#-0,61 (9)
#3,34 (10)
#ACT/B (11
#X
#4
#68,86750
#63,2700
#253,08
#-22,39
#-8,13
#-0,41
#8,27 (line 20)
#ACT/C
#X
#15
#10,33132
#4,18
#62,70
#-92,27
#-59,54
#0,00
#2,03';
The code:
// Explode.
$array = explode('#', $string);
// Should trim all values to f.e. remove new lines.
$array = array_map('trim', $array);
// Should filter empty values (due to empty lines in string).
$array = array_filter($array, 'strlen');
// Split into chunks.
$array = array_chunk($array, 10, true);
Output:
echo var_export($array, true) . PHP_EOL;
// [
// 0 => [
// 1 => 'ACT/A (line 1)',
// 2 => 'XXX (2)',
// 3 => '2 (3)',
// 4 => '51,6844 (4)',
// 5 => '50,7000 (5)',
// 6 => '101,40 (6)',
// 7 => '-1,97 (7)',
// 8 => '-1,91 (8)',
// 9 => '-0,61 (9)',
// 10 => '3,34 (10)',
// ],
// 1 => [
// 11 => 'ACT/B (11',
// 12 => 'X',
// 13 => '4',
// 14 => '68,86750',
// 15 => '63,2700',
// 16 => '253,08',
// 17 => '-22,39',
// 18 => '-8,13',
// 19 => '-0,41',
// 20 => '8,27 (line 20)',
// ],
// 2 => [
// 21 => 'ACT/C',
// 22 => 'X',
// 23 => '15',
// 24 => '10,33132',
// 25 => '4,18',
// 26 => '62,70',
// 27 => '-92,27',
// 28 => '-59,54',
// 29 => '0,00',
// 30 => '2,03',
// ],
// ]

problems getting values in my multidimensional arrays

I'm having problems getting values in my multidimensional arrays php
$shop = array(
array(
Title => "rose",
Price => 1.25,
Number => 15
),
array(
Title => "daisy",
Price => 0.75,
Number => 25,
),
array(
Title => "orchid",
Price => 1.15,
Number => 7
)
);
And
$titlearray = array('rose','daisy');
And Now. I want check Compare 2 array;
If have value $titlearray in $shop return True or false.
Example:
$titlearray = array('rose','daisy'); return TRUE
$titlearray = array('rose','daisy','kool'); return FALSE
plz help me. Thanks for watching.
Seems simple enough.
$titles = array_map(function($i) {return $i['Title'];},$shop);
return !array_diff($titlearray,$titles);

how explode country code from number php

how explode country code from number php, i need 1-3 characters from number for country validation. I have array with ( code => country), i have script example, but it works only if i explode 1 character, but country code can be 2 or 3 characters too. i know that i can explode by $number = explode(" ", 1 777777); , but i need full number without additional symbols or spaces.
$number = "1777777";
$we = substr($number, 0, 2);
$countrys = array(
'1' => 'us',
'2' => 'uk',
'3' => 'de',
'44' => 'fi', // dont work if 2 characters..
'123' => 'no' // dont work if 3 characters..
);
$array = "$we";
echo $country = $countrys[$array[0]];
echo "<br>country";
how i can modificate this code? thank you! or any other suggestions?
I would assume if the number is like 123777777, the country code would be considered as 123 instead of 12 or 1, if both 1, 12 and 123 exist.
In this case, just start by checking the first 3 digits against the countrys array. If none is found, then go on by checking the first 2 digits, and so on.
$number = "123777777";
$countrys = array(
'1' => 'us',
'2' => 'uk',
'3' => 'de',
'44' => 'fi',
'123' => 'no',
);
$i = 3;
$country = "";
while ($i > 0) {
if (isset($countrys[substr($number, 0, $i)])) {
$country = $countrys[substr($number, 0, $i)];
break;
} else {
$i--;
}
}
echo $country;
In your code, you are always assigning the first country in $countrys to your $country. So you will always end up with us.
Instead of doing that, you should get the first 3, 2, or 1 digit(s) from your phone number, and check if the array key exists. If it does, you use that as the lookup index to your array.
Here's a pseudocode for you to get started:
perform a loop to get the first 3, 2, and 1 digit(s) from the phone number:
// Order is important as you want to match '123' before you match '1'
Check if the array key for the digit exists in the $countrys
If yes, get the country name and quit the loop
If not, go to the next less digit
If the key is found:
Print the country name
Else:
Print the country is not found
Here is partial code for how you'd do it for the 3 digits. You just need to add a loop (such as for-loop) to simplify your work.
$number = "123777777";
// Add your loop here to check for 3, 2, 1 digit
$we = substr($number, 0, 3);
$countrys = array(
'1' => 'us',
'2' => 'uk',
'3' => 'de',
'44' => 'fi',
'123' => 'no'
);
$array = "$we"; // You don't need this
if (array_key_exists ($we, $countrys))
{
// Remember to break out of your loop when the country is found
$country = $countrys[$we];
}
else
{
$country = "Non in list";
}
// End your loop here
echo "${we} is ${country}\n";
Use krsort to sort the array (longest numbers first). In this case "123" will be checked before "1". Then you can simply iterate over your array and check if the number begins with the country's number. If it matches, save the country code to a variable:
$number = '123777777';
$countrys = array(
'1' => 'us',
'2' => 'uk',
'3' => 'de',
'44' => 'fi',
'123' => 'no'
);
// sort the array by key, descending
krsort($countrys, SORT_NUMERIC);
// iterate over all countries
foreach ($countrys as $we => $ccode) {
// if number begins with current number
if (strpos($number, '' . $we) === 0) {
// store country code and break the loop
$country = $ccode;
break;
}
}
echo 'country: ' . $country; // expected: "no"

Finding maximum path of a complex array

I have an array:
$data = array(
1 => array(
"time" => 1,
"parent" => array(4)
),
2 => array(
"time" => 3,
"parent" => array(4, 5)
),
3 => array(
"time" => 2,
"parent" => array(6)
),
4 => array(
"time" => 1,
"parent" => array(6)
),
5 => array(
"time" => 1,
"parent" => array(4)
),
6 => array(
"time" => 1,
"parent" => array()
)
);
Key is the ID of an element, parent is an array of elements, which refers to element id and time is just an integer.
This is an illustrated example of a given array:
Schema
The integer on the bottom-left is the "id" and the integer in the middle is "time".
My goal here is find the most time-consuming path of this array. In the given example, the path would be 2->5->4->6 (id wise) resulting in 6 "time" overall. It looks pretty easy on paper, however I can't really seem to code an algorythm to get the elements of the most time-consuming path. I would appreciate any kind of help.
I think the algorythm should be kind of bruteforce-ish and check through all of the options available. Thus with the given array it would go like:
1 -> 4 -> 6 = 3
2 -> 4 -> 6 = 5
2 -> 5 -> 4 -> 6 = 6
3 -> 6 = 3
4 -> 6 = 2
5 -> 4 -> 6 = 3
Thanks in advance.
Note that this will only work if there are no loops in the array.
// Note: built this in the SO editor, might have bugs
$cached = [];
$arrays = []; // Do this yourself
function get_path($num) {
global $arrays, $cached;
if (isset($cached[$num])) return $cached[$num];
$array = $arrays[$num];
$maxtime = $array['time'];
$bestpath = array($num);
foreach ($array['parent'] as $i) {
$path = get_path($i);
if ($path['time']+$array['time'] > $maxtime) {
$maxtime = $path['time'] + $array['time'];
$bestpath = array_merge(array($num),$path['path']);
}
}
$cached[$num] = array('path' => $bestpath, 'time' => $maxtime);
return $cached[$num];
}
var_dump(get_path(5));
Not really a bruteforce way, should be close enough to O(n). The basic idea is that you just cache the paths it can take.
Note: I used a bit of C-style syntax here, but ideally you wouldn't actually write the code like this

Stuck on trying to turn input into a string of certain format

So I have a field in the db for delivery times, and it is of certain format (not my design for sure!):
Here are a couple of examples:
M, T, W, TH ; MTWTH 9 TO 4 CLOSED 12 TO 1
T, W, TH, F ; TTHF 9 TO 5 CLOSED 12 TO 2, W 9 TO 12
T, W, TH, F ; T 10 TO 7 CLOSED 2 TO 3, WTH 9 TO 6 CLOSED 1 TO 2, F 8 TO 5 CLOSED 1 TO 2
So basically, you have a list of days, separated by a comma, followed by semi-colon.
After semi-colon there is a list of days, not separated, and followed by delivery times including break times.
The problem is, delivery times can be different for each day, same for all days, or same for let's say Monday and tuesday, then different for Wednesday, and same for Thursday and Friday (but Thursday and Friday have different delivery times then Monday and Tuesday).
And if delivery times match for some days, those days should be grouped together, with delivery times listed after the group days, then other matched or single days listed after that. First day for which there are delivery hours specified must go first.
I know, the format is ridiculous and delivery days/times should be in a separate table linked by organization id, but it's the format they use now for some download files.
I hope to change it later, but for now...
I can't figure out how to transform input from 4 drop-downs (From, To, Closed From, Closed To) into that format.
Any of the days may not have closing times at all. At least one day must have delivery times specified (I check for that).
I of course got the first part (before semi-colon) - that's easy, after that I'm totally stuck.
If all days for which there's delivery had the same hours, I wouldn't have issues. But since it can be totally different from day to day, I don't know how to do it.
So this is the processing block so far:
$trimmed is the array of POST values that has been trimmed,
$trimmed['1Monday'] is Monday From, $trimmed['2Monday'] is Monday To,
$trimmed['3Monday'] is Monday Closed From, $trimmed['4Monday'] is Monday Closed To.
So $M_times variable will have open/closing times for Monday.
$days_del array is used to later create a string that is the first part of required format.
I can get the first day with some times specified and I can try to catch any matches after that, but that isn't useful if first delivery day has different values from the rest, yet there are some matching values after that. I'm not even sure that I need to use anoither (numbered) days array or I can do it with just the main one with M-F for keys.
<?php
$days = array('M' => 'Monday', 'T' => 'Tuesday', 'W' => 'Wednesday', 'TH' => 'Thursday', 'F' => 'Friday');
$days_numbered = array(1 => 'M', 'T', 'W', 'TH', 'F');
$days_del = $matches = $full_list = array();
$days_delivery2 = '';
$count_no_del = 0; # initialize count for days with no delivery
$times_6 = '';
foreach ($days as $k => $v){ ##### Beginning of days loop
if (isset($trimmed["1$v"]) && isset($trimmed["2$v"])){ # If both closing and opening times have been specified
$days_del[] = $k;
${$k.'_times'} = $trimmed["1$v"] . ' TO ' . $trimmed["2$v"];
if ((!isset($trimmed["3$v"]) && isset($trimmed["4$v"])) || (isset($trimmed["3$v"]) && !isset($trimmed["4$v"]))){ # If only beginning or only end of lunch(?) break has been specified
$errors[] = 'Delivery times for $v - please specify both start and end of closing time.';
}elseif (isset($trimmed["3$v"]) && isset($trimmed["4$v"]){ # If both beginning and end of lunch(?) breakh have been specified
${$k.'_times'} .= 'CLOSED' . $trimmed["3$v"] . ' TO ' . $trimmed["4$v"];
}
}elseif ((!isset($trimmed["1$v"]) && isset($trimmed["2$v"])) || (isset($trimmed["1$v"]) && !isset($trimmed["2$v"]))){ # If only closing or only opening time has been specified
$errors[] = 'Delivery times for $v - please specify both start and end of delivery time.';
${$k.'_times'} = NULL;
}elseif (!isset($trimmed["1$v"]) && !isset($trimmed["2$v"])){ # No delivery times specified for this day
${$k.'_times'} = NULL;
$count_no_del++;
}
$full_list["$k"] = ${$k.'_times'};
} ##### End of days loop
if ($count_no_del > 0){ # If there are no delivery days specified
$errors[] = 'You must specify delivery hours for at least one day of the week.';
}
$days_delivery1 = implode(',', $days_del);
$days_delivery1 = $days_delivery1 . ' ; ';
foreach ($days_numbered as $num => $val){ # Getting first day for which delivery hours have been specified
if (isset(${$val.'_times'}) && (${$val.'_times'} != NULL)){
${'times_'.$num} = ${$val.'_times'};
$first_day = $num;
break;
}
}
$check_array = array_keys($full_list, ${'times_'.$first_day})); # checking how many other days match the delivery hours for the first specified day.
foreach ($check_array as $array_key){
$days_delivery2 .= $array_key;
}
$days_delivery2 .= " " . ${'times_'.$first_day};
$note_line = $days_delivery1 . " " ; # second part, something like 'MTH 9 To 5, TW 10 TO 5 CLOSED 1 TO 2, F 10 TO 2' should go a s the second part of the string.
?>
As you can see, after getting the first part of the string in that format ($days_delivery1) I'm stuck and don't know what the hell am I doing. I have a vouge idea of using 2 different arrays (main one and numbered one) and using array_keys to find matching values, but any time I try to work on it I just run into a wall. Any ideas would be much apreciated.
The thing that is potentially difficult here is grouping like entries for the second section. In order to overcome this, I suggest you convert the times to 4 digit representations to normalise them and concatenate the patterns, so create a "signature" for each pattern.
In the below example, I will be working on the principal that the following $_POST arrays will result in the corresponding result strings:
$_POST1 = array(
'1Monday' => 9,
'2Monday' => 5,
'3Monday' => 12,
'4Monday' => 1,
'1Tuesday' => 9,
'2Tuesday' => 5,
'3Tuesday' => 12,
'4Tuesday' => 1,
'1Wednesday' => 9,
'2Wednesday' => 5,
'3Wednesday' => 12,
'4Wednesday' => 1,
'1Thursday' => 9,
'2Thursday' => 5,
'3Thursday' => 12,
'4Thursday' => 1,
'1Friday' => 9,
'2Friday' => 4,
'3Friday' => 11,
'4Friday' => 12
);
$result1 = "M, T, W, TH, F ; MTWTH 9 TO 5 CLOSED 12 TO 1, F 9 TO 4 CLOSED 11 TO 12";
$_POST2 = array(
'1Monday' => '',
'2Monday' => '',
'3Monday' => '',
'4Monday' => '',
'1Tuesday' => 9,
'2Tuesday' => 5,
'3Tuesday' => 12,
'4Tuesday' => 1,
'1Wednesday' => 9,
'2Wednesday' => 5,
'3Wednesday' => 12,
'4Wednesday' => 1,
'1Thursday' => 9,
'2Thursday' => 5,
'3Thursday' => 12,
'4Thursday' => 1,
'1Friday' => 9,
'2Friday' => 5,
'3Friday' => 12,
'4Friday' => 1
);
$result2 = "T, W, TH, F ; TWTHF 9 TO 5 CLOSED 12 TO 1";
Now let's take a look at how we'd process that data. I'm going to assume that we are working directly with the unprocessed POST array.
<?php
// Obviously we will need this map if the inputs use the full day names
$days = array('M' => 'Monday', 'T' => 'Tuesday', 'W' => 'Wednesday', 'TH' => 'Thursday', 'F' => 'Friday');
// A couple of arrays to hold the results of the loop iterations
$resultDays = $resultTimes = array();
// First we iterate over the days
foreach ($days as $dayCode => $dayFull) {
// Data about this day
$dayData = array(
'open' => 0,
'close' => 0,
'lunchClose' => 0,
'lunchOpen' => 0,
'days' => array()
);
// First get the open/close times
$open = $_POST["1$dayFull"];
$close = $_POST["2$dayFull"];
if (empty($open) || empty($close)) {
// If we don't have both open/close times, skip this day
continue;
}
// We definitely open on this day
$resultDays[] = $dayCode;
$dayData['open'] = $open;
$dayData['close'] = $close;
// Pad the strings to make the signature
$openPadded = str_pad($open, 2, "0", STR_PAD_LEFT);
$closePadded = str_pad($close, 2, "0", STR_PAD_LEFT);
// Now look at lunch times
$lunchClose = $_POST["3$dayFull"];
$lunchOpen = $_POST["4$dayFull"];
if (!empty($lunchClose) || !empty($lunchOpen)) {
// If we have both open/close times, add a lunch break
// Add data to $dayData
$dayData['lunchClose'] = $lunchClose;
$dayData['lunchOpen'] = $lunchOpen;
// Pad the strings to make the signature
$lunchClosePadded = str_pad($lunchClose, 2, "0", STR_PAD_LEFT);
$lunchOpenPadded = str_pad($lunchOpen, 2, "0", STR_PAD_LEFT);
} else {
// So we don't break this signature
$lunchClosePadded = $lunchOpenPadded = '';
}
// Build the signature
$signature = $openPadded.$closePadded.$lunchClosePadded.$lunchOpenPadded;
// Add day data to result times array
if (!isset($resultTimes[$signature])) {
$resultTimes[$signature] = $dayData;
}
$resultTimes[$signature]['days'][] = $dayCode;
}
// Now we can build the string
// Like you say, first part is easy
$firstPart = implode(', ', $resultDays);
// Loop $resultTimes and construct to more sensible arrangements
$secondPart = array();
foreach ($resultTimes as $block) {
$str = implode('', $block['days'])." {$block['open']} TO {$block['close']}";
if (!empty($block['lunchClose']) && !empty($block['lunchOpen'])) {
$str .= " CLOSED {$block['lunchClose']} TO {$block['lunchOpen']}";
}
$secondPart[] = $str;
}
// Now we can construct the final string
$finalResult = $firstPart." ; ".implode(', ', $secondPart);
See it working.
Well, that was fun.

Categories