Generate all possible combinations - php

I have an app where users can customize the product they are going to purchase by choosing options from a menu. This menu has many sections, and each section may have a list of checkboxes for multichoice, or radiobuttons when only one option can be selected. The user must select at least one option in each section. The menu structure is something like this:
$sections = array();
$sections[1] = array(
'multichoice' => true,
'options' => array('A','B','C')
);
$sections[2] = array(
'multichoice' => false,
'options' => array('A','B','C','D')
);
$sections[3] = array(
'multichoice' => false,
'options' => array('A','B')
);
$sections[4] = array(
'multichoice' => true,
'options' => array('A','B','C','D','E')
);
Example: Sandwich is the product. Type of bread is one "section" of choice. You may want light bread, dark bread, milk bread or vegan bread. Only one option can be chosen under this section. Now in the "salad" section, you may choose more than one type of salad to add to the bread.
Now, my boss asked for me to create a page listing all possible combinations in case the user is too lazy to build the product himself. So I must be able to generate a structure like this:
$combinations = array(
array(
1 => array('A','B'),
2 => 'A',
3 => 'A',
4 => array('B','D','E')
),
array(
1 => array('A'),
2 => 'B',
3 => 'A',
4 => array('A','B')
)
// etc...
);
I have managed to find all possible combinations using a random approach, generating hashes for comparision with what has already been generated. This actually works, but runs very slowly (this is brute-force basically):
...
function generate(){
$result = array();
$ids = array();
foreach($this->getSections() as $sect){
$items = $this->getSectionOptions($sect['id']);
if($sect['multi']=='N'){
$item = $items[rand(0, count($items)-1)];
$result[$sect['id']] = $item['id'];
$ids[] = $item['id'];
} else {
$how_many = rand(1,count($items));
shuffle($items);
for($i=1;$i<=$how_many;$i++){
$item = array_shift($items);
$result[$sect['id']][] = $item['id'];
$ids[] = $item['id'];
}
}
}
sort($ids);
return array(
'hash' => implode(',',$ids),
'items' => $result
);
}
function generateMany($attempts=1000){
$result = array();
$hashes = array();
for($i=1;$i<=$attempts;$i++){
$combine = $this->generate();
if(!in_array($combine['hash'],$hashes)){
$result[] = $combine['items'];
$hashes[] = $combine['hash'];
}
}
return $result;
}
...
I want your help to create something more precise and faster. Remember that each combination must have at least one option of each section. And also keep in mind that the order of options in multichoice sections is irrelevant, (i.e. E,B,A is the same as B,E,A)
Thanks

Thanks this was a puzzle really fun to do!
So how did I solve, recursion recursion recursion :D
I've started with the multi choice since it is the hardest one! (and actually it will solve also the mixing)
Explanation
To explain how I got it to work, lets take an example with choices A, B, C. We would have then the following combinations:
A B
A B C
A C
B
B C
C
If we take a look closer, we can see some how a pattern. Lets take the result list the first element (A)
B
B C
C
---
B
B C
C
Hmm, interesting... Now lets take again the first element (B)
C
---
C
It's a simple case but this will happen in any size case.
So I've made the recursion script to get to the end, then adding backwards, the iteration combination and duplicating it with the previous value.
And voila! this is it!
For the final mix where all the elements are demanded, I've made a very similar method, but it must contain 1 element of each
And it's quite fast!
Total time for 100000 iterations with 126 combinations: 14.410287857056 seconds
if you find any mistake ping me :D
Code
https://gist.github.com/MLoureiro/a0ecd1ef477e08b6b83a

Related

Polymorph String To Pattern

I'm working on an issue where users (truck drivers in this case) use SMS to send in information about work status. I want to keep the keying simple as not all users have smart phones so I have adopted some simple short codes for their input. Here are some examples and their meanings:
P#123456-3 (This is for picking up load 123456-3)
D#456789-1 (For the dropping of load 456789-1)
L#345678-9 (Load 345678-9 is going to be late)
This is pretty simple but users (and truck drivers) being what they are will key the updates in somewhat deviant manners such as:
#D 456789-1
D# 456789 - 1
D#.456789-1 This load looks wet to me do weneed to cancelthis order
You can pretty much come up with a dozen other permutations and it's not hard for me to catch and fix those that I can imagine.
I mostly use regular expressions to test the input against all my imagined "bad" patterns and then extract what I assume are the good parts, reassembling them into the correct order.
It's the new errors that cause me problems so I got to wondering if there was a more generic method where I can pass a "pattern" and a "message" to a function that would do it's best to turn the "message" into something matching the "pattern".
My searches have not found anything that really fits what I'm trying to do and I'm not even sure if there is a good general way to do this. I happen to be using PHP for this implementation but any type of example should help. Do any of you have a method?
If the user has problems with your software, fix the software, not the user!
The problem arises because your format looks unnecessary complicated. Why do you need the hash in the first place? How about simplifying it down to the following:
operation-code maybe-space load-number maybe-space and comment
Operation codes are assigned to different phone keys, so that J, K and L mean the same thing. Load-numbers can be sent as digits and as letters as well, e.g. agja means 2452. It's hard for the user to make a mistake using this format.
Here's some code to illustrate this approach:
function parse($msg) {
$codes = array(
3 => 'DROP',
5 => 'LOAD',
// etc
);
preg_match('~(\S)\s*(\S+)(\s+.+)?~', $msg, $m);
if(!$m)
return null; // cannot parse
$a = '.,"?!abcdefghijklmnopqrstuvwxyz';
$d = '1111122233344455566677777888999';
return array(
'opcode' => $codes[strtr($m[1], $a, $d)],
'load' => intval(strtr($m[2], $a, $d)),
'comment' => isset($m[3]) ? trim($m[3]) : ''
);
}
print_r(parse(' j ww03 This load looks wet to me'));
//[opcode] => LOAD
//[load] => 9903
//[comment] => This load looks wet to me
print_r(parse('dxx0123'));
//[opcode] => DROP
//[load] => 990123
//[comment] =>
Try something like this:
function parse($input) {
// Clean up your input: 'D#.456789 - 1 foo bar' to 'D 456789 1 foo far'
$clean = trim(preg_replace('/\W+/', ' ', $input));
// Take first 3 words.
list($status, $loadId1, $loadId2) = explode(' ', $clean);
// Glue back your load ID to '456789-1'
$loadId = $loadId1 . '-' . $loadId2;
return compact('status', 'loadId');
}
Example:
$inputs = array(
'P#123456-3',
'#D 456789-1',
'D# 456789 - 1',
'D#.456789-1 This load looks wet to me do weneed to cancelthis order',
);
echo '<pre>';
foreach ($inputs as $s) {
print_r(parse($s));
}
Output:
Array
(
[status] => P
[loadId] => 123456-3
)
Array
(
[status] => D
[loadId] => 456789-1
)
Array
(
[status] => D
[loadId] => 456789-1
)
Array
(
[status] => D
[loadId] => 456789-1
)
First, remove stuff that shouldn't be there:
$str = preg_replace('/[^PDL\d-]/i', '', $str);
That gives you the following normalised results:
D456789-1
D456789-1
D456789-1ldlddld
Then, attempt to match the data you want:
if (preg_match('/^([PDL])(\d+-\d)/i', $str, $match)) {
$code = $match[1];
$load = $match[2];
} else {
// uh oh, something wrong with the format!
}
Something like
/^[#\s]*([PDL])[#\s]*(\d+[\s-]+\d)/
or to be even more relaxed,
/^[^\d]*([PDL])[^\d]*(\d+)[^\d]+(\d)/
would get you what you want. But I'd prefer HamZa's comment as a solution: throw it back and tell them to get their act together :)

PHP create array with different sort fields

First of all thanks to stackoverflow to provide this plattform and you, to give a newbie a help ;-)
Now ..
I have 2 tables: pages and sections
Each has an own id.
A page has one or more sections.
A section belongs to exactly 1 page.
The list sequence of both id's is handled in a different field.
I read both tables and create an (unsorted) array.
What I finally need is a sorted list as shown below, having the page_id's and section_id's in the correct sequence..
Here an example of my tarray after retrieve data:
myArr[] = array( page_id=>2, section_id=>2, parent_id=>0, level=>0, page_seq=>1, section_seq=>2, page_title=>p1 );
myArr[] = array( page_id=>2, section_id=>9, parent_id=>0, level=>0, page_seq=>1, section_seq=>1, page_title=>p1 );
myArr[] = array( page_id=>3, section_id=>3, parent_id=>0, level=>0, page_seq=>2, section_seq=>1, page_title=>p2 );
myArr[] = array( page_id=>4, section_id=>4, parent_id=>0, level=>0, page_seq=>3, section_seq=>1, page_title=>p3 );
myArr[] = array( page_id=>5, section_id=>5, parent_id=>3, level=>1, page_seq=>3, section_seq=>1, page_title=>p2-3 );
myArr[] = array( page_id=>6, section_id=>6, parent_id=>3, level=>1, page_seq=>2, section_seq=>1, page_title=>p2-2 );
myArr[] = array( page_id=>7, section_id=>7, parent_id=>4, level=>1, page_seq=>1, section_seq=>1, page_title=>p3-1 );
myArr[] = array( page_id=>8, section_id=>8, parent_id=>7, level=>2, page_seq=>1, section_seq=>1, page_title=>p3-1-1 );
myArr[] = array( page_id=>9, section_id=>10, parent_id=>5, level=>2, page_seq=>1, section_seq=>1, page_title=>p2-1-1 );
myArr[] = array( page_id=>9, section_id=>11, parent_id=>5, level=>2, page_seq=>1, section_seq=>2, page_title=>p2-1-1 );
myArr[] = array( page_id=>10, section_id=>12, parent_id=>3, level=>1, page_seq=>1, section_seq=>1, page_title=>p2-1 );
My problem is the sorting.
The section_seq is a sequence of the section within the page.
The page_seq is a sequence of the page within the level for the same parent.
I find some rekursive loop examples already here, but - to be honest - I'm not able to adapt them to my needs. And, do I need a rekursive loop ?
What should be the key of my array: the section_id, because it is unique over all pages ?
How to do the correct sort ?
Just to be noted: the page title could unfortunately not to be used - as in the example above - for sorting purposes ;-) as it is free text...
So what I need is:
read first page (1) with level 0 and page_seq = 1
read first page (2) having level 1 -if exists- with page (1) as parent and page_seq = 1
read first page (3) having level 2 -if exists- with page (2) as parent and page_seq = 1
... continue as long as no more deeper level exists
read second page (4) having level 2 -if exists- with page (2) as parent and page_seq = 1
... continue as long as no more deeper level exists and also no more pages on this level having page (2) as parent
read second page (5) having level 1 -if exists- with page (1) as parent and page_seq = 1
... continue as long as no more deeper level exists and also no more pages on this level having page (5) as parent
read second page (6) with level 0 and page_seq = 2
and so on.
Any powerfull help & ideas ?
Thanks in advance
Wolfgang
My idea would be to use the database to do the sorting. With MySQL you could use a left join on both tables to get the sections and pages data in one results table. In the query you could use the ORDER BY keyword to specify the column(s) according to which the results must be sorted.
So finally I found a solution
1) create a list of all pages to be process. The $key generated looks on first level like "1", on 2nd level like "1.1", on 3rd level like "1.1.1" etc.
read all pages
while ( $fetch_obj_pages = $obj_pages->fetchRow() ):
// do some validations and keep only those in mind which should be processed
....
// get and save key for sorting
if ( $isValid == TRUE ):
$key = '';
if ( array_key_exists ( $fetch_obj_pages[ 'parent_id' ], $pageSortList ) == TRUE ):
$key = $pageSortList[ $fetch_obj_pages[ 'parent_id' ]] . '.';
endif;
$key .= $fetch_obj_pages[ 'page_seq' ];
$pageSortList[ $fetch_obj_pages[ 'page_id' ]] = $key;
endif;
endwhile;
2) create a list of all sections to process. It is taken the key generated in pages list above and enhance it with section sorting sequence, then save it per section_id as key.
read all sections
while ( $fetch_obj_sections = $obj_sections->fetchRow() ):
// do some validations and keep only those in mind which should be processed
....
// get and save key for sorting
if ( $isValid == TRUE ):
// get key from page and save key for section
$key = '';
if ( array_key_exists ( $fetch_obj_sections[ 'page_id' ], $pageSortList ) == TRUE ):
$key = $pageSortList[ $fetch_obj_sections[ 'page_id' ]] . '-';
endif;
$key .= $fetch_obj_sections[ section_seq ];
$sectionSortList[ $fetch_obj_sections[ 'section_id' ]] = $key;
endif;
endwhile;
3) sort the section list
natsort ( $sectionSortList );
4) building the result by looping over $sectionSortList and getting the neccessary data due to section_id used as key in this list.
Before someone ask: Of course, all possible filters are done in the SQL. But some are not possible and due to need to be done in a loop-
Most probably, step 1) and 2) could be done in one step using a JOIN and a proper ORDER BY.
Last-Bit-Saving-Junkies will find lot of places for code optimizations and reduction.
However, I like the alternate functionality with : and endif; ;-)
In case someone sees a possibility to make things really faster, feel welcome to provide your ideas.

Using an array (or xml) to program a function

Ultimate Goal
Is to make something like Magento offers - basically a logic builder, and as shown by this post on Stackoverflow: jQuery (or any web tool) Nested Expression Builder So far I have made jQuery to build a tree and get the data that I want the builder to use, check, and set. Now I just need to parse the checks and add it into various places in a script I'm making - but I am unsure how to process it dynamically so that these checks can be performed, which will lead to some actions occurring/data being changed automatically.
Maybe we can call this dynamic expression processing?
Original Post
Forgive me, I know what I would like to do, but have little idea how to do it - so I'm looking for some inspiration. I have allowed a multidimensional array to be generated, and the array would contain certain 'commands' and logic functions, and when a condition is true it is executed.
In it's most basic form, the array would contain a set of if statements, where if the statement were true, then would would proceed to the next array item and go down a level, if it were false, then you'd proceed to the next array item with no children (an unmarried sibling, i guess we could call it). Once there is nothing left to process, since nothing is true, then nothing would happen.
I'd imagine that maybe the best way to 'feed' the data in would be via XML - though would this be possible, I mean, to keep going deeper, else go down, essentially until there is a true condition?
Basically, the array takes the following form (though I not 100% sure I've written it correctly, but I think it looks right :s):
[0][0] => array('function' => 'if', 'check' => 'day', 'condition' => 'equals', 'value' => '3');
[0][1][0] => array('function' => 'set', 'name' => 'date_day', 'value' => 'wednesday');
[1][0] => array('function' => 'if', 'check' => 'day', 'condition' => 'equals', 'value' => '4');
[1][1][0] => array('function' => 'set', 'name' => 'date_day', 'value' => 'thursday');
So the above would be - if day=3, then set date_day as wednesday; else if day=4, then set date_day as thursday
Which I'd imagine would correspond to (though i have no idea if you can sub item):
<items>
<item>
<function>if</function>
<check>day</check>
<condition>equals</condition>
<value>3</value>
<item>
<function>set</function>
<name>date_day</name>
<value>wednesday</value>
</item>
</item>
<item>
<function>if</function>
<check>day</check>
<condition>equals</condition>
<value>4</value>
<item>
<function>set</function>
<name>date_day</name>
<value>thursday</value>
</item>
</item>
</items>
Which would basically make the following statements in a function of some sort:
function ($current_data){
LOOP
if(FUNCTION == "if"){
if(CHECK CONDITION VALUE){
**go to next item deeper in the chain**
} else {
**go to sibling item**
}
} else if(FUNCTION == "set"){
define(NAME, VALUE);
}
ENDLOOP
}
I know the above can be done using the date() function, but this is a very basic example. Another example could involve check to see if the colour entered was red, and if it were, then set something based on this colour, else do something else if it were blue. Another could be to set the template to be for US visitors if the US flag was clicked on. The point is that it could basically fulfil any action and do a check and give a result - basically like programming - but where the function data is feed in by PHP or XML
I'm sure there must be something out there that can accomplish this, but I just have no idea were to start exactly, so any assistance would be great - and yes I know there could be some security concerns, but I plan on having checks in place checking that the checks, conditions, values, etc are safe (so this needs to be able to be factored in).
Many many thanks!
Okay JSON vs. XML aside, here's how I would process that array...
$array = xmldecode($xml);
$resultFound = false;
$i = 0;
while(!$resultFound && $i < count($array)) {
if (myFunction($array[$i]) {
$resultFound = true;
}
$i++;
}
if (!$resultFound) {
// error condition
}
function myFunction($array) {
$function = $array[0]['function'];
switch($function) {
case 'if':
$checkVariable = $array[0]['check'];
$condition = $array[0]['condition'];
$checkValue = $array[0]['value'];
switch($checkVariable)
case 'day':
switch($condition) {
case 'equals':
if (GLOBAL_DAY == $checkValue) {
return myFunction($array[1]);
} else {
return false;
}
break;
case 'less than':
if (GLOBAL_DAY < $checkValue) {
return myFunction($array[1]);
} else {
return false;
}
break;
}
break;
}
break;
case 'set':
$setVariable = $array[0]['name'];
$setValue = $array[0]['value'];
switch($setVariable) {
case 'date_day':
GLOBAL_DATE_DAY = $setValue;
return true;
break;
}
break;
}
}
Your problem is very similar to that of form validation, so I would look at some popular jQuery form validation plugins or MVC frameworks like CakePHP. These typically all have stock building blocks for the most common validation rules that the user can easily put together and pass specific arguments to to cover most scenarios.
The notable difference between your problem and these examples is that form validation frameworks are aimed at developers, so they can simply write custom functions if they need to glue multiple rules together to form a more complex rule. However, you can still achieve something that works for probably 98% of all use cases by doing something like:
$offers = array(
'pet feed sale' => array(
'category' => array(1, 2, 3)
'total' => '>100',
'weekday' => array('mon', 'wed')
'set' => array(
'discount' => 80
'shipping' => 0
)
),
'medication sale' => array(
'category' => 4
'date' => '2012-1-28',
'set' => array(
'discount' => 50
)
)
);
And if the user needs apply more complex pricing structures then they could, for instance, break the "pet feed sale" rule into 3 offers, one for dog food, one for cat food, and one for fish food. There might be more repetition, but it makes it much easier to implement than a full parser.
Also, most non-programmers probably handle repetition a lot better than complicated logic and control flow.

Indented list to multidimensional array

I was surprised not to find an answer to this on SO (or elsewhere on the internet for that matter). It concerns a nested indented list which I want to convert into a multidimensional array according to the level of indentation.
By way of an example, here is some sample input:
Home
Products
Product 1
Product 1 Images
Product 2
Product 2 Images
Where to Buy
About Us
Meet the Team
Careers
Contact Us
Ideally I'd like to feed this into some (recursive?) function and get the following output:
array(
'Home' => array(),
'Products' => array(
'Product 1' => array(
'Product 1 Images' => array(),
),
'Product 2' => array(
'Product 2 Images' => array(),
),
'Where to Buy' => array(),
),
'About Us' => array(
'Meet the Team' => array(),
'Careers' => array(),
),
'Contact Us' => array(),
);
I'm confused by the logic required to perform such a task, so any help would be appreciated.
As it's still unclear if you're trying to read from some given structure (html-dom) or from the given string as plain text, I assumed it's the string you're trying to parse. If so, try:
<?php
$list =
'Home
Products
Product 1
Product 1 Images
Product 2
Product 2 Images
Where to Buy
About Us
Meet the Team
Careers
Contact Us';
function helper($list, $indentation = ' ') {
$result = array();
$path = array();
foreach (explode("\n", $list) as $line) {
// get depth and label
$depth = 0;
while (substr($line, 0, strlen($indentation)) === $indentation) {
$depth += 1;
$line = substr($line, strlen($indentation));
}
// truncate path if needed
while ($depth < sizeof($path)) {
array_pop($path);
}
// keep label (at depth)
$path[$depth] = $line;
// traverse path and add label to result
$parent =& $result;
foreach ($path as $depth => $key) {
if (!isset($parent[$key])) {
$parent[$line] = array();
break;
}
$parent =& $parent[$key];
}
}
// return
return $result;
}
print_r(helper($list));
Demo: http://codepad.org/zgfHvkBV
I'm not going to write the recursive function, but to point you in a helpful direction, take a look at PHP's substr_count function. Based on this, you could count the number of tabs in front of each line and compare it with the number of tabs in the previous line to figure out if it's a child, sibling, etc.
Am I the only one with a beautiful mind seeing the pattern?
The input array is almost the same thing!, with the lines ending in only 3 possible ways: opening, whole, or closing parentheses.
$L = 4;//estimate depth level
function tabbed_text_to_array ($raw, $L) {
$raw = preg_replace("/^(\\t*)([^\\t\\n]*)\\n?/m" , "\t$1'$2' => array(\n" , $raw );
for( ; $L > 0 ; $L-- ) {
for( $i=0; $i<3 ;$i++ ) {
$preL = $L-1;
$s = array( "^(\\t{{$L}})([^\\t\\),]*)(?=\\n\\t{{$L}}')", "^(\\t{{$L}})([^\\t\\),]*)(?=\\n(\\t{0,{$preL}})')", "^(\\t{{$L}})(\\),)(?=\\n(\\t{{$preL}})[^\t])" );
$r = array( "$1$2)," , "$1$2)\n" . str_repeat("\t",$preL) . ")," , "$1),\n$3)," );
$raw = preg_replace( "/$s[$i]/m" , $r[$i], $raw );
}
}
return "array(\n". $raw. ")\n);";
}
This function generates a string with a literal array. Then you just eval() it.
It's not as bad as it looks. The double backslashes makes the reading harder, but it's simple.
Like cell reproduction, it first adds in one pass the most common things: the quotation marks, commas and opening parentheses, and then it adds the smaller details in two more passes. All of them run once for each level you specify (You could waste code in finding out the deepest level to start processing from, but a guess is enough.)
See the working demo / tool to convert tab-indented text into array
Or visit a php fiddle with all the passes, so you can expand on this.
There is a class on PHP Scripts that will do what you need. You can find it here:
Array To List
It takes a multidimensional array and creates the HTML.
Updated
The opposite to this was actually required. To do this, it's probably best to use a DOMDocument object and load the HTML into a object representation.
http://php.net/manual/en/domdocument.loadhtml.php

Ordering an Array with weights?

The problem
Right now I have to create this:
In my database; each favorite as its own 'weight' entry.
As of right now it defaults to '10'
Let's say I have an array of favorites,
the weights go like this accordingly:
1
2
3
4
If I were to press the 'down' arrow on '2' I'd have to add +1 to its weight.
Which would result in:
1
3
3
4
But I just want to switch favorite '2' with favorite '3's weight.
I need a way to ensure that when the weights for each item function as they're implied.
Since just adding or subtracting 1 will lead to a lot of problem.
Hopefully I explained it properly.
The code
Writing to the database
/*
* Write Form data to database
*/
function f25_favorites_form_submit($form, &$form_state){
global $user;
$listOfPaths = f25_favorites_listOfPaths();
$selected = $form_state['values']['path'];
$data = array(
'uid' => $user->uid,
'path' => $selected,
'title' => $listOfPaths[$selected]['#title'],
'weight' => 10,
'timestamp' => time(),
);
drupal_write_record(f25_favorites, $data);
}
The Query
/*
* Fetching Specific User's Favorites Data
*/
function f25_favorites_user_favorites() {
global $user;
if($user->uid > 0){
$userid = $user->uid;
$user_paths = db_query("SELECT * FROM f25_favorites foo WHERE foo.uid = '%s' ORDER BY foo.weight DESC", $userid);
$pathlist = array();
while ($data = db_fetch_object($user_paths)) {
$pathlist[] = $data;
}
return $pathlist;
}
}
How should I approach this problem?
if all your favorites are weighted differently, you can simply swap the weight field of the one "getting up" with the one "getting down", or reverse, when decreasing weight. you'd have to figure out the code though
as there are only little items I would save the whole order to the database, not only what has been moved.: i don't know much about drupal but i think it would be easy to accomplish as drupal is php and javascript based.
each element in html has its own id/index (uid?)
<div id="el_1">Menu1</div>
<div id="el_2">Menu2</div>
use javascript to get list of your items in the reordered order (like:
itm[0]=item1;
itm[1]=item3;
itm[2]=item2;
itm[3]=item4;
send itm with ajax to your php updater. and update your db, use the position received and not weights.
now your db has something like this:
uid;position
1;0
3;1
2;2
4;3
I recommend you jquery for the javascript work, jquery has a nice sortable plugin. but it is also awesome for the ajax stuf.

Categories