0

I have string structured like

(cat,dog,fish) && (drinks) && (milk,water)

I need to convert to an array list like

cat drinks milk
cat drinks water
dog drinks milk
dog drinks water
fish drinks milk
fish drinks water

I've thought about doing it with a loop that takes each group and inserts them into an array like

0th pass: fill the array with the first row

(cat,dog,fish) && (drinks) && (milk,water)

1st pass: detect the first group and split it while removing the source

cat && (drinks) && (milk,water) 
dog && (drinks) && (milk,water) 
fish && (drinks) && (milk,water)

2nd pass ....

then loop this each time take the line split it add it to the end and remove the original.

Do you have a better idea? and in PHP?

For those who wonder It is part of sentence parsing code I'm writing.

Thanks

4
  • 1
    sounds like you have the right idea, so go write some code :-) Commented May 31, 2012 at 20:27
  • 2
    but.. but.. fish don't drink milk :( Commented May 31, 2012 at 20:27
  • yeah but my code wouldn't be recursive it would be a repeat loop till there is nothing left to process :-) I will have to either teach fish to drink milk or change the code. Commented May 31, 2012 at 20:35
  • is this how you structure the string? $string = "(cat,dog,fish) && (drinks) && (milk,water)"; Commented May 31, 2012 at 20:40

2 Answers 2

2

use normal string parsing to get 3 arrays which correspond to their groupings. I think you can figure it out with explode()

Then generate the "cartesian product" of the 3 arrays

$iterators = array(
    new ArrayIterator(array('cat', 'dog', 'fish'))
  , new ArrayIterator(array('drinks'))
  , new ArrayIterator(array('milk', 'water'))
);
$citer = new CartesianProductIterator($iterators);
foreach ($citer as $combo) {
    printf("[%s]\n", join(',', $combo));
}

Using

class CartesianProductIterator implements Iterator {
    protected $iterators;

    function __construct(array $iters) {
        $this->iterators = $iters;
    }

    function rewind() {
        foreach ($this->iterators as $it) {
            $it->rewind();
        }
    }

    function current() {
        $values = array();
        foreach ($this->iterators as $it) {
            $values[] = $it->current();
        }
        return $values;
    }

    function key() {
        return null;
    }

    function next() {
        /*      
        loop them in reverse, but exclude first
        why? example, odometer: 55199
        you always check the rightmost digit first to see if incrementing it would roll it over and need to be "rewound" to 0, 
        which causes the digit to the left to increase as well, which may also cause it to roll over as well, and so on...
        looping in reverse operates from right column to the left.
        we dont rewind the first column because if the leftmost column is on its last element and needs to roll over
        then this iterator has reached its end, and so rewind() needs to be explicitly called 
        */
        for ($i = count($this->iterators) - 1; $i > 0; --$i) {
            $it = $this->iterators[$i];
            $it->next();
            if ($it->valid()) {
                // were done advancing because we found a column that didnt roll over
                return;
            } else {
                $it->rewind();
            }
        }

        //if execution reached here, then all of the columns have rolled over, so we must attempt to roll over the left most column
        $this->iterators[0]->next();
    }

    function valid() {
        return $this->iterators[0]->valid();
    }
}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the solution, I didn't know about ArrayIterator before. I ended up using the other solution. But your solution is very elegant I couldn't accept both :-(
1

Once i needed to make every combination of similar sets. I had a recursive function which was actually very resource-intensive on big array (9 parts containing 5 items each) but i can try to adjust it for you:

$input=array(array("cat","dog","fish"),array("drinks"),array("milk","water"));
$output=array();
function combination($string,$level)
{
  global $output;
  global $input;
  if (isset($input[$level]))
  {
    $item=$input[$level];
    if (is_array($item))
    {
      foreach ($item as $i)     
        combination($string." ".$i,$level+1);
    }
    else
      combination($string." ".$item,$level+1);
  }
  else
    $output[]=$string;
}
combination("",0);
var_export($output);

However converting your string into input array is different problem, which i am not sure how to solve so i will keep it up to you.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.