Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Jan 27th, 2008, 8:59 PM   #1
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 420
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Function too convoluted?

Made this function to filter out results from an array of arrays:
php Syntax (Toggle Plain Text)
  1. <pre>
  2. <?php
  3. $data = array(
  4. array('volume' => 67, 'edition' => 2),
  5. array('volume' => 86, 'edition' => 1),
  6. array('volume' => 85, 'edition' => 6),
  7. array('volume' => 98, 'edition' => 2),
  8. array('volume' => 86, 'edition' => 6),
  9. array('volume' => 67, 'edition' => array(
  10. array('volume' => 67, 'edition' => 2),
  11. array('volume' => 86, 'edition' => 1),
  12. array('volume' => 85, 'edition' => 6),
  13. array('volume' => 98, 'edition' => 2),
  14. array('volume' => 86, 'edition' => 6),
  15. array('volume' => 67, 'edition' => array(
  16. array('volume' => 67, 'edition' => 2),
  17. array('volume' => 86, 'edition' => 1),
  18. array('volume' => 85, 'edition' => 6),
  19. array('volume' => 98, 'edition' => 2),
  20. array('volume' => 86, 'edition' => 6),
  21. array('volume' => 67, 'edition' => 7)
  22. ))
  23. ))
  24. );
  25.  
  26. $data = attenuate($data, 'edition', 'GTLT', 1, 9, 'volume', 'gt', 70);
  27. print_r($data);
  28.  
  29. function attenuate(){
  30. switch(func_num_args()){
  31. case 0:
  32. return false;
  33. break;
  34. case 1:
  35. if(is_array(reset(func_get_args()))) return reset(func_get_args());
  36. else return false;
  37. break;
  38. default:
  39. $args = func_get_args();
  40. $data = array_shift($args);
  41. if(!is_array($data)) return false;
  42. if(is_array(reset($args))) $args = array_pop($args);
  43. $attenParam = array();
  44. reset($args);
  45. do{
  46. $temp = array();
  47. array_push($temp, current($args)); // attenuation key
  48. array_push($temp, next($args)); // attenuation range
  49. // attenuation values
  50. if(preg_match('/^((l|g)t){0,1}(eq){0,1}i*$/i', end($temp)))
  51. array_push($temp, array(next($args)));
  52. elseif(preg_match('/^((l|g)t)(?(?<=lt)(eq){0,1}gt(eq){0,1}|(eq){0,1}lt(eq){0,1})i*$/i', end($temp)))
  53. array_push($temp, array(next($args), next($args)));
  54. else
  55. return false;
  56. next($args);
  57. array_push($attenParam, $temp);
  58. }while(key($args) !== null);
  59.  
  60. foreach($data as $dataKey => $dataValue){
  61. if(is_array($dataValue)){
  62. $dataValue = attenuate($dataValue, $args);
  63. if(count(array_diff_assoc($data[$dataKey], $dataValue)) > 0)
  64. unset($data[$dataKey]);
  65. }else{
  66. foreach($attenParam as $value){
  67. $key = reset($value);
  68. if(!strcmp($dataKey, $key)){
  69. $attenOp = next($value);
  70. $attenVal = end($value);
  71. $caseSensitive = true;
  72. if(stripos($attenOp, 'i') !== false) $caseSensitive = false;
  73. $attenOp = preg_replace('/i*/i', '', $attenOp);
  74. foreach($attenVal as $attenKey => $attenCond){
  75. if(is_string($dataValue)){
  76. if($caseSensitive)
  77. $attenVal[$attenKey] = strnatcmp($dataValue, $attenCond);
  78. else
  79. $attenVal[$attenKey] = strnatcasecmp($dataValue, $attenCond);
  80. }else{
  81. if($dataValue < $attenCond)
  82. $attenVal[$attenKey] = -1;
  83. elseif($dataValue > $attenCond)
  84. $attenVal[$attenKey] = 1;
  85. else
  86. $attenVal[$attenKey] = 0;
  87. }
  88. }
  89. if(!strcasecmp('EQ', $attenOp)){
  90. if(reset($attenVal) != 0)
  91. unset($data[$dataKey]);
  92. }elseif(!strcasecmp('GT', $attenOp)){
  93. if(reset($attenVal) != 1)
  94. unset($data[$dataKey]);
  95. }elseif(!strcasecmp('GTEQ', $attenOp)){
  96. if(reset($attenVal) == -1)
  97. unset($data[$dataKey]);
  98. }elseif(!strcasecmp('LT', $attenOp)){
  99. if(reset($attenVal) != -1)
  100. unset($data[$dataKey]);
  101. }elseif(!strcasecmp('LTEQ', $attenOp)){
  102. if(reset($attenVal) == 1)
  103. unset($data[$dataKey]);
  104. }elseif(!strcasecmp('GTLT', $attenOp)){
  105. if(reset($attenVal) < 1 || end($attenVal) > -1)
  106. unset($data[$dataKey]);
  107. }elseif(!strcasecmp('GTEQLT', $attenOp)){
  108. if(reset($attenVal) < 0 || end($attenVal) > -1)
  109. unset($data[$dataKey]);
  110. }elseif(!strcasecmp('LTGT', $attenOp)){
  111. if(reset($attenVal) > -1 && end($attenVal) < 1)
  112. unset($data[$dataKey]);
  113. }elseif(!strcasecmp('LTEQGT', $attenOp)){
  114. if(reset($attenVal) > 0 && end($attenVal) < 1)
  115. unset($data[$dataKey]);
  116. }elseif(!strcasecmp('GTLTEQ', $attenOp)){
  117. if(reset($attenVal) < 1 || end($attenVal) > 0)
  118. unset($data[$dataKey]);
  119. }elseif(!strcasecmp('GTEQLTEQ', $attenOp)){
  120. if(reset($attenVal) < 0 || end($attenVal) > 0)
  121. unset($data[$dataKey]);
  122. }elseif(!strcasecmp('LTGTEQ', $attenOp)){
  123. if(reset($attenVal) > -1 && end($attenVal) < 0)
  124. unset($data[$dataKey]);
  125. }elseif(!strcasecmp('LTEQGTEQ', $attenOp)){
  126. if(reset($attenVal) > 0 && end($attenVal) < 0)
  127. unset($data[$dataKey]);
  128. }
  129.  
  130. }
  131. }
  132. }
  133. }
  134. return $data;
  135. break;
  136. }
  137. }
  138. ?>
  139. </pre>
I don't know seems like I'm doing too much, works well though. Here's the output
Array
(
    [2] => Array
        (
            [volume] => 85
            [edition] => 6
        )

    [3] => Array
        (
            [volume] => 98
            [edition] => 2
        )

    [4] => Array
        (
            [volume] => 86
            [edition] => 6
        )

)
Suggestions for improvement?
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jan 27th, 2008, 9:40 PM   #2
Sane
Programming Guru
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 1,840
Rep Power: 5 Sane will become famous soon enough
Send a message via MSN to Sane
Re: Function too convoluted?

Yikes. That does not look like a healthy piece of code at all.

Could you specify the input/output in the format of a problem statement?

More specifically, for every possible inputted array, what is the desired output? I can't bother myself to decipher your cryptic code. It would be easier if you explained.
Sane is offline   Reply With Quote
Old Jan 27th, 2008, 10:45 PM   #3
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 420
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Re: Function too convoluted?

Input: array of values or of nested arrays
Procedure: unset values that are =, <, >, <=, >= (and permutations of those) of some range
Output: filtered array
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jan 27th, 2008, 11:31 PM   #4
titaniumdecoy
Expert Programmer
 
titaniumdecoy's Avatar
 
Join Date: Nov 2005
Posts: 843
Rep Power: 3 titaniumdecoy is on a distinguished road
Send a message via AIM to titaniumdecoy
Re: Function too convoluted?

I would create a function for each sort descriptor and build an array of containing references to the appropriate sort descriptors.
titaniumdecoy is offline   Reply With Quote
Old Jan 27th, 2008, 11:49 PM   #5
Sane
Programming Guru
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 1,840
Rep Power: 5 Sane will become famous soon enough
Send a message via MSN to Sane
Re: Function too convoluted?

I'm not sure if it's the same thing as what titaniumdecoy is saying, but I'm sure your code would be much more stable, readable, extendable, and simpler, if you were to write a callback function that does the desired filtering, and pass that callback function into 'attenuate'.

Attenuate then asks the callback function whether or not a specific item should be filtered.

Then it's left up to the one who's implementing the function to modify the callback, or pass through his/her own callback. It's also more powerful than leaving it up to the limitations of your debilitating 'GT/LT/EQ' scheme.
Sane is offline   Reply With Quote
Old Jan 28th, 2008, 10:09 AM   #6
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 420
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Re: Function too convoluted?

Attenuate is the filtering function. It doesn't make much sense to me to write another function that would aid this one. I'd still have to know how they want to filter the results so I don't understand how the GT/LT/EQ is debilitating. I can explain algorithmically what the function is doing.
  1. If 0 arguments are passed return an error
  2. If 1 argument is passed return the array or return error
  3. If more than one argument is passed assume the first argument is an array, if not return error
  4. If remaining arguments were passed as an array pop them from the passed arguments array
  5. Take the first argument of the remaining args and assume it is the key to filter by
  6. Take the second argument of the remaining args and assume it is the operation to be performed
  7. If the operation is LT|GT|EQ|LTEQ|GTEQ then only take one argument as the filter value
  8. If the the operation is LTGT|GTLT|LTEQGT|GTEQLT|LTGTEQ|GTLTEQ|LTEQGTEQ|GTEQLTEQ then take the next two arguments as the filtering value
  9. Repeat until there are no more arguments left
  10. Look at each value of the array, if it's an array then recurse the function
  11. If the returned result is different from the value then unset that portion of the array
  12. If the value is not an array then proceed to filter results only if the key passed through parameters is the same as the current key of the data
  13. If an i was present in the filtering operation then assume we want case insensitivity of strings
  14. Check the actual value to determine if it is a string or not
  15. Irrelevant of the type of checking denote that a value greater results in 1 a value less than results in -1 and equality results in 0
  16. Based on the previous results and the operation type determine the range of values which satisfy the filtering operation and unset any that do not
  17. Return the filtered array
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jan 28th, 2008, 10:53 AM   #7
Sane
Programming Guru
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 1,840
Rep Power: 5 Sane will become famous soon enough
Send a message via MSN to Sane
Re: Function too convoluted?

But you have one function that processes the array. And in that same function, you test whether or not it's a valid item to include. If you want to modify the method of filtering (not the values involved) you need to modify the entire outerworking of the same function that's doing the processing. That's bad coding.

Pass through a static callback function that will determine whether or not its argument is a valid item. The parts of your program that change become isolated from the static parts. Just leave it up to the one who's writing the callback function to worry about how it's being filtered.

If these specifications need to be set dynamically on run-time, according to the user's request, then you can work your way around that. But it's silly to invent your own ASCII logic gates to be interpreted with yet more boolean logic.

With this change you don't need to worry about your own wonky optional parameters. It would make your code capable of being changed in a second flat. The code would be much shorter and clearer. It would also fix your nest of around 11 indents for some of the blocks (horrific!). I suggest you stick to conventional coding unless there's a clear reason why it can not work more efficiently any other way.

As an aside, this very much reminds me of something from over a year ago... I went through almost this exact same situation as you are going through now. I wrote a hash table that would take optional parameters, in its lookup() function, to filter results on a set of logical conditions. I thought I was being clever because it replicated behaviour similar to MySQL, which does use ASCII logic.

But what did not occur to me, is my (brilliant) own logic scheme became absolutely useless as soon as I tried to interface a database client over top. My client could not control the optional parameters that went through, because optional parameters are specified statically on run-time. My method only worked as long as all interfacing to the lookup function was hard-coded.

The reason this logic scheme works for MySQL is because it's not as though their string is parsed into a list of parameters and then passed through a function, and reinterpreted. It's all handled in a very direct way. Presumably with some very clever algorithms too, such as the Horn formula framework. This isn't the only reason your custom approach can fail; it can be plain out difficult to work with, or the requirements of the algorithm can change. In which case you might need to extend the inner portion of your function to be even worse off than it is right now.
Sane is offline   Reply With Quote
Old Jan 31st, 2008, 11:48 AM   #8
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 420
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Re: Function too convoluted?

Alright Sane this is what I managed to come up with after checking out the PHP site and reading what you were saying. I didn't quite understand you at first until I found this example on the PHP site.
PHP Syntax (Toggle Plain Text)
  1. <pre>
  2. <?php
  3. $data = array(
  4. array('volume' => 67, 'edition' => 2),
  5. array('volume' => 86, 'edition' => 1),
  6. array('volume' => 85, 'edition' => 6),
  7. array('volume' => 98, 'edition' => 2),
  8. array('volume' => 86, 'edition' => 6),
  9. array('volume' => 67, 'edition' => array(
  10. array('volume' => 67, 'edition' => 2),
  11. array('volume' => 86, 'edition' => 1),
  12. array('volume' => 85, 'edition' => 6),
  13. array('volume' => 98, 'edition' => 2),
  14. array('volume' => 86, 'edition' => 6),
  15. array('volume' => 67, 'edition' => array(
  16. array('volume' => 67, 'edition' => 2),
  17. array('volume' => 86, 'edition' => 1),
  18. array('volume' => 85, 'edition' => 6),
  19. array('volume' => 98, 'edition' => 2),
  20. array('volume' => 86, 'edition' => 6),
  21. array('volume' => 67, 'edition' => 7)
  22. ))
  23. ))
  24. );
  25.  
  26. $callback_func = create_function('$key, $value', 'return ($value < 2 || $value > 70) ? true : false;');
  27.  
  28. print_r(array_filter_multi($data, $callback_func, null));
  29.  
  30. function array_filter_multi($array, $callback, $filtered_output = ""){
  31. $ret = array();
  32. foreach($array as $key=>$value){
  33. if($callback($key,$value)){
  34. if(is_array($value)){
  35. $ret[$key] = array_filter_multi($value, $callback, $filtered_output);
  36. }elseif(is_object($value)){
  37. $ret[$key] = array_filter_multi(get_object_vars($value), $callback, $filtered_output);
  38. }else{
  39. $ret[$key]=$value;
  40. }
  41. }else{
  42. $ret[$key]=$filtered_output;
  43. }
  44. }
  45. return $ret;
  46. }
  47. ?>
  48. </pre>
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jan 31st, 2008, 12:39 PM   #9
Sane
Programming Guru
 
Sane's Avatar
 
Join Date: Apr 2005
Location: Waterloo, Ontario
Posts: 1,840
Rep Power: 5 Sane will become famous soon enough
Send a message via MSN to Sane
Re: Function too convoluted?

Very nice!

If that still meets your initial requirements, I'd say that's a much nicer solution, yes?

And if you don't want to worry about the bad garbage cleanup of "create_function", you could also pass through a named function like so, I believe...

PHP Syntax (Toggle Plain Text)
  1. function callback_func($key, $value) { return ($value < 2 || $value > 70); }
  2.  
  3. print_r(array_filter_multi($data, $callback_func, null));

I'm not as sure about that. But it should work.
By the way, ? true : false is a redundant statement.
Sane is offline   Reply With Quote
Old Jan 31st, 2008, 3:22 PM   #10
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 420
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Re: Function too convoluted?

Ok here's what all of this was intended to do:
PHP Syntax (Toggle Plain Text)
  1. <pre>
  2. <?php
  3. $data = array(
  4. array('volume' => 67, 'edition' => 2),
  5. array('volume' => 86, 'edition' => 1),
  6. array('volume' => 85, 'edition' => 6),
  7. array('volume' => 98, 'edition' => 2),
  8. array('volume' => 86, 'edition' => 6),
  9. array('volume' => 67, 'edition' => array(
  10. array('volume' => 67, 'edition' => 2),
  11. array('volume' => 86, 'edition' => 1),
  12. array('volume' => 85, 'edition' => 6),
  13. array('volume' => 98, 'edition' => 2),
  14. array('volume' => 86, 'edition' => 6),
  15. array('volume' => 67, 'edition' => array(
  16. array('volume' => 67, 'edition' => 2),
  17. array('volume' => 86, 'edition' => 1),
  18. array('volume' => 85, 'edition' => 6),
  19. array('volume' => 98, 'edition' => 2),
  20. array('volume' => 86, 'edition' => 6),
  21. array('volume' => 67, 'edition' => 7)
  22. ))
  23. ))
  24. );
  25.  
  26. $callback_func = create_function('$key, $value', 'return ($value < 8 || $value > 70);');
  27.  
  28. $output = array_filter_multi($data, $callback_func, null);
  29. $output = array_transect($output, $data);
  30. $output = array_liken($output, $data);
  31. $output = cull($output, array(array('volume', SORT_ASC), array('edition', SORT_DESC)));
  32. print_r($output);
  33.  
  34. function array_liken($new, $old){
  35. $ret = array();
  36. reset($new);
  37. reset($old);
  38. while(null !== $key = key($new)){
  39. $newValue = current($new);
  40. $oldValue = $old[$key];
  41. if(is_array($newValue)){
  42. if(count($newValue) == count($oldValue))
  43. $ret[$key]=$newValue;
  44. array_liken($newValue, $oldValue);
  45. }elseif(is_object($newValue)){
  46. if(count(get_object_vars($newValue)) == count(get_object_vars($oldValue))