0

I need to look through Array1, finding any matching sequences from Array2, and update corresponding sub-arrays in Array1.

I previously had help on a similar question; Find array in array, in sequence

The previous solutions worked fantastically - but this time I'm dealing with more complicated data, and I need to update the Haystack array (rather than simply return the matches).

Array 1 : Haystack

Array ( 
    [0] => Array ( [v1] => aa   [v2] => ) 
    [1] => Array ( [v1] => bb   [v2] => ) 
    [2] => Array ( [v1] => cccc [v2] => ) 
    [3] => Array ( [v1] => bb   [v2] => ) 
    [4] => Array ( [v1] => aa   [v2] => ) 
    [5] => Array ( [v1] => bb   [v2] => ) 
    [6] => Array ( [v1] => cccc [v2] => ) 
    [7] => Array ( [v1] => bb   [v2] => ) 
) 

Array 2 : Needles

Array ( 
    [0] => Array ( [aa] => nnnn [bb] => nnn [cccc] =>n )
    [1] => Array ( [aa] => ddd  [bb] => dd )
)

Thus I should find "aa bb cccc" (needle[0]) in the haystack, and update the array to become;

Array ( 
    [0] => Array ( [v1] => aa   [v2] => nnnn ) 
    [1] => Array ( [v1] => bb   [v2] => nnn ) 
    [2] => Array ( [v1] => cccc [v2] => n ) 
    [3] => Array ( [v1] => bb   [v2] => ) 
    [4] => Array ( [v1] => aa   [v2] => ) 
    [5] => Array ( [v1] => bb   [v2] => ) 
    [6] => Array ( [v1] => cccc [v2] => ) 
    [7] => Array ( [v1] => bb   [v2] => ) 
) 

The 2 versions of code I have are;

Code version 1:

// cache array sizes
$haystack_len = count($haystack);
$needle_len = count($needle);

// shortlist the possible starting keys
$possible_keys = array_keys($haystack, $needle[0], true);

$results = array();

foreach ($possible_keys as $index) {
    // start searching
    $i = $index; $j = 0;
    while ($i < $haystack_len && $j < $needle_len) {
        if ($haystack[$i] !== $needle[$j]) {
            continue 2; // no match
        }
        ++$i; ++$j;
    }
    // match
    $results[] = range($index, $index + $needle_len - 1);
}

print_r($results);

and Code version 2:

function find_array_in_array($needle, $haystack) {
    $keys = array_keys($haystack, $needle[0]);
    $out = array();
    foreach ($keys as $key) {
        $add = true;
        $result = array();
        foreach ($needle as $i => $value) {
            if (!(isset($haystack[$key + $i]) && $haystack[$key + $i] == $value)) {
                $add = false;
                break;
            }
            $result[] = $key + $i;
        }
        if ($add == true) { 
            $out[] = $result;
        }
    }
    return $out;
}

But these are designed to work with flat arrays;

$haystack = array('a', 'b', 'a', 'b', 'c', 'c', 'a', 'b', 'd', 'c', 'a', 'b', 'a', 'b', 'c');
$needle = array('a', 'b', 'c');

Instead, I need them to work with the arrays as per the top (nested, and the needles are looking for matches to the needle[key] to the haystack[array][v1]

Though I've fiddled and faffed with the earlier code, I cannot beat it into the right shape :( I keep going through foreach loops to access things, and tried using for() etc.

foreach ($needlebox as $needles){
    foreach ($needles as $needlekey=>$needlevalue){
        foreach ($haystack as $haystackkey=>$haystackvalues){

            // insert above methods

        }
    }
}

but I hit the following issues; 1) Array2 (Needles) is huge, with the same needles appearing multiple times? 2) I only get a single match (even if Array 1 contains multiple matches to Array 2 Needle-n - it only finds a) the first or b) the last) 3) It matches regardless of order/sequence (I think I broke the code somehow, and it will match "cccc bb aa", when that order in Needles does not exist (it's instead "aa bb cccc").

I've now spent 2 days faffing with this wondering what I've done wrong.

I've attempted to use both solutions (the foreach and the for methods) ... but I cannot get either of them to work.

2
  • 1
    It is not very clear to me what you are trying to achieve. Will you be using all "needles" or just a specific one (e.g why are you not setting "aa" to "ddd" and "bb" to "dd" in your example at the top) ? Commented Jun 20, 2013 at 19:24
  • @ExpertSystem - sorry if it wasn't that clear. Basically, there is a big nested array (haystack), full of sub arrays. I need to find matches from a collection of smaller arrays, and use the value of the smaller sub arrays (needles) to update Haystack. :: If it helps, think of a scenario like looking through orders and updating a customer address - I look through the Client data (haystack), and update matches of postcode (needles). Hope that is clearer. Commented Jun 20, 2013 at 20:27

1 Answer 1

1

If I understand correctly what you are trying to achieve, you could do it like this (see comments in code):

/* Process one needle (look into haystack 
   and modify it accordingly) */
function processNeedle(&$haystack, $needle) {
    $needleKeys   = array_keys($needle);
    $needleValues = array_values($needle);
    $needleLen    = count($needle);
    $haystackLen  = count($haystack);

    /* Find indexes where a match begins */
    $matches = array();
    for ($i = 0; $i < ($haystackLen - $needleLen + 1); $i++) {
        $match = true;
        for ($j = 0; $j < $needleLen; $j++) {
            if ($haystack[$i + $j]["v1"] != $needleKeys[$j]) {
                $match = false;
                break;
            }
        }
        if ($match) {
            $matches[] = $i;
            $i += $needleLen - 1;
        }
    }

    /* Do the actual replacement for all matches */
    forEach ($matches as $startIdx) {
        for ($j = 0; $j < $needleLen; $j++) {
            $haystack[$startIdx + $j]["v2"] = $needleValues[$j];
        }
    }
}

See, also, this short demo.

4
  • That looks great (and the linked to page is great). The problem I've gfot is I'm stuck with 2 nested arrays - and the only way I can see is to use this code in a bunch of nested foreachs... is that right? Commented Jun 20, 2013 at 20:31
  • Okay - adapted slightly due to circumstance (in a class, had to add a foreach loop to get the needles from the object ...) ... but it Works!!! Thank you so very much @ExperSystem. Commented Jun 20, 2013 at 20:56
  • A quick followup ... I've adapted it to include in an existing Class. When I go through a large Haystack (1K items) and a lot of needles (5K), it's taking approx 5secs ... is that expected? Is there any obvious way to speed it up, or is it par of the course with this sort of iteration (due to the nesting etc.)? :: And again, Thank You @ExpertSystem - it's fantastic! Commented Jun 21, 2013 at 14:38
  • okay - alteration1 :: for ($i = 0; $i < ($haystackLen - $needleLen + 1); $i++) { // == // $i = 0; $n= ($haystackLen - $needleLen + 1); for (; $i < $n; $i++) { :: alteration2 :: for ($j = 0; $j < $needleLen; $j++) { // == // $j = 0; for (; $j < $needleLen; $j++) { :: alteration3 :: placed the last foreach (replacements) in the if($match) chunk ... this has shaved off approx 0.3 seconds when using 1K haystack and 5K needles ... I don't think it can be improved any more than that? (talk about micro improvements :D) Commented Jun 21, 2013 at 16:55

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.