[ 'col1' =>'val1', 'col2' => 'val2' ], 'row2' => [ 'col1' => 'val3', 'col2' => 'val4' ] ]; $this->assertEquals( ArrayLib::invert($arr), [ 'col1' => [ 'row1' => 'val1', 'row2' => 'val3', ], 'col2' => [ 'row1' => 'val2', 'row2' => 'val4', ], ] ); } public function testValuekey() { $this->assertEquals( ArrayLib::valuekey( [ 'testkey1' => 'testvalue1', 'testkey2' => 'testvalue2' ] ), [ 'testvalue1' => 'testvalue1', 'testvalue2' => 'testvalue2' ] ); } public function testArrayMapRecursive() { $array = [ 'a ', [' b', 'c'], ]; $strtoupper = [ 'A ', [' B', 'C'], ]; $trim = [ 'a', ['b', 'c'], ]; $this->assertEquals( $strtoupper, ArrayLib::array_map_recursive('strtoupper', $array) ); $this->assertEquals( $trim, ArrayLib::array_map_recursive('trim', $array) ); } public function testArrayMergeRecursive() { $first = [ 'first' => 'a', 'second' => 'b', ]; $second = [ 'third' => 'c', 'fourth' => 'd', ]; $expected = [ 'first' => 'a', 'second' => 'b', 'third' => 'c', 'fourth' => 'd', ]; $this->assertEquals( $expected, ArrayLib::array_merge_recursive($first, $second), 'First values should supplement second values' ); $first = [ 'first' => 'a', 'second' => 'b', ]; $second = [ 'first' => 'c', 'third' => 'd', ]; $expected = [ 'first' => 'c', 'second' => 'b', 'third' => 'd', ]; $this->assertEquals( $expected, ArrayLib::array_merge_recursive($first, $second), 'Second values should override first values' ); $first = [ 'first' => [ 'first' => 'a', ], 'second' => [ 'second' => 'b', ], ]; $second = [ 'first' => [ 'first' => 'c', ], 'third' => [ 'third' => 'd', ], ]; $expected = [ 'first' => [ 'first' => 'c', ], 'second' => [ 'second' => 'b', ], 'third' => [ 'third' => 'd', ], ]; $this->assertEquals( $expected, ArrayLib::array_merge_recursive($first, $second), 'Nested second values should override first values' ); $first = [ 'first' => [ 'first' => 'a', ], 'second' => [ 'second' => 'b', ], ]; $second = [ 'first' => [ 'second' => 'c', ], 'third' => [ 'third' => 'd', ], ]; $expected = [ 'first' => [ 'first' => 'a', 'second' => 'c', ], 'second' => [ 'second' => 'b', ], 'third' => [ 'third' => 'd', ], ]; $this->assertEquals( $expected, ArrayLib::array_merge_recursive($first, $second), 'Nested first values should supplement second values' ); $first = [ 'first' => [ 0 => 'a', ], 'second' => [ 1 => 'b', ], ]; $second = [ 'first' => [ 0 => 'c', ], 'third' => [ 2 => 'd', ], ]; $expected = [ 'first' => [ 0 => 'c', ], 'second' => [ 1 => 'b', ], 'third' => [ 2 => 'd', ], ]; $this->assertEquals( $expected, ArrayLib::array_merge_recursive($first, $second), 'Numeric keys should behave like string keys' ); } public function testFlatten() { $options = [ '1' => 'one', '2' => 'two' ]; $expected = $options; $this->assertEquals($expected, ArrayLib::flatten($options)); $options = [ '1' => [ '2' => 'two', '3' => 'three' ], '4' => 'four' ]; $expected = [ '2' => 'two', '3' => 'three', '4' => 'four' ]; $this->assertEquals($expected, ArrayLib::flatten($options)); } /** * Test that items can be added during iteration */ public function testIterateVolatileAppended() { $initial = [ 'one' => [ 'next' => 'two', 'prev' => null ], 'two' => [ 'next' => 'three', 'prev' => 'one' ], 'three' => [ 'next' => null, 'prev' => 'two' ], ]; // Test new items are iterated $items = $initial; $seen = []; foreach (ArrayLib::iterateVolatile($items) as $key => $value) { $seen[$key] = $value; // Append four if ($key === 'three') { $items['three']['next'] = 'four'; $items['four'] = [ 'next' => null, 'prev' => 'three']; } // Prepend zero won't force it to be iterated next, but it will be iterated if ($key === 'one') { $items['one']['next'] = 'zero'; $items = array_merge( ['zero' => [ 'next' => 'one', 'prev' => 'three']], $items ); } } $expected = [ 'one' => [ 'next' => 'two', 'prev' => null ], 'two' => [ 'next' => 'three', 'prev' => 'one' ], 'three' => [ 'next' => null, 'prev' => 'two' ], 'zero' => [ 'next' => 'one', 'prev' => 'three'], 'four' => [ 'next' => null, 'prev' => 'three'] ]; // All items are iterated (order not deterministic) $this->assertEquals( $expected, $seen, 'New items are iterated over' ); } /** * Test that items can be modified during iteration */ public function testIterateVolatileModified() { $initial = [ 'one' => [ 'next' => 'two', 'prev' => null ], 'two' => [ 'next' => 'three', 'prev' => 'one' ], 'three' => [ 'next' => 'four', 'prev' => 'two' ], 'four' => [ 'next' => null, 'prev' => 'three' ], ]; // Test new items are iterated $items = $initial; $seen = []; foreach (ArrayLib::iterateVolatile($items) as $key => $value) { $seen[$key] = $value; // One modifies two if ($key === 'one') { $items['two']['modifiedby'] = 'one'; } // Two removes three, preventing it from being iterated next if ($key === 'two') { unset($items['three']); } // Four removes two, but since it's already been iterated by this point // it's too late. if ($key === 'four') { unset($items['two']); } } $expected = [ 'one' => [ 'next' => 'two', 'prev' => null ], 'two' => [ 'next' => 'three', 'prev' => 'one', 'modifiedby' => 'one' ], 'four' => [ 'next' => null, 'prev' => 'three' ], ]; // All items are iterated (order not deterministic) $this->assertEquals( ksort($expected), ksort($seen), 'New items are iterated over' ); } public function testShuffleAssociative() { $list = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'f' => 6]; $copy = $list; // Try shuffling 3 times - it's technically possible the result of a shuffle could be // the exact same order as the original list. for ($attempts = 1; $attempts <= 3; $attempts++) { ArrayLib::shuffleAssociative($copy); // Check value/key association is retained foreach ($list as $key => $value) { $this->assertEquals($value, $copy[$key]); } $failed = false; try { // Check the order is different $this->assertNotSame($list, $copy); } catch (ExpectationFailedException $e) { $failed = true; // Only fail the test if we've tried and failed 3 times. if ($attempts === 3) { throw $e; } } // If we've passed the shuffle test, don't retry. if (!$failed) { break; } } } }