Asked  7 Months ago    Answers:  5   Viewed   79 times

I'm trying to do something but I can't find any solution, I'm also having some trouble putting it into works so here is a sample code, maybe it'll be enough to demonstrate what I'm aiming for:

$input = array
(
    'who' => 'me',
    'what' => 'car',
    'more' => 'car',
    'when' => 'today',
);

Now, I want to use array_splice() to remove (and return) one element from the array:

$spliced = key(array_splice($input, 2, 1)); // I'm only interested in the key...

The above will remove and return 1 element (third argument) from $input (first argument), at offset 2 (second argument), so $spliced will hold the value more.

I'll be iterating over $input with a foreach loop, I know the key to be spliced but the problem is I don't know its numerical offset and since array_splice only accepts integers I don't know what to do.

A very dull example:

$result = array();

foreach ($input as $key => $value)
{
    if ($key == 'more')
    {
        // Remove the index "more" from $input and add it to $result.
        $result[] = key(array_splice($input, 2 /* How do I know its 2? */, 1));
    }
}

I first though of using array_search() but it's pointless since it'll return the associative index....

How do I determine the numerical offset of a associative index?

 Answers

84

Just grabbing and unsetting the value is a much better approach (and likely faster too), but anyway, you can just count along

$result = array();
$idx = 0; // init offset
foreach ($input as $key => $value)
{
    if ($key == 'more')
    {
        // Remove the index "more" from $input and add it to $result.
        $result[] = key(array_splice($input, $idx, 1));
    }
    $idx++; // count offset
}
print_R($result);
print_R($input);

gives

Array
(
    [0] => more
)
Array
(
    [who] => me
    [what] => car
    [when] => today
)

BUT Technically speaking an associative key has no numerical index. If the input array was

$input = array
(
    'who' => 'me',
    'what' => 'car',
    'more' => 'car',
    'when' => 'today',
    'foo', 'bar', 'baz'
);

then index 2 is "baz". But since array_slice accepts an offset, which is not the same as a numeric key, it uses the element found at that position in the array (in order the elements appear), which is why counting along works.

On a sidenote, with numeric keys in the array, you'd get funny results, because you are testing for equality instead of identity. Make it $key === 'more' instead to prevent 'more' getting typecasted. Since associative keys are unique you could also return after 'more' was found, because checking subsequent keys is pointless. But really:

if(array_key_exists('more', $input)) unset($input['more']);
Saturday, May 29, 2021
 
nasty
answered 7 Months ago
52

Your current code is good for two cases:

  1. Reading all data as is, without modification
  2. Reading all data and performing a universal modification

Since what you need is a conditional modification, it seems like you would be better off creating the structure of the array manually. Doing so would add another benefit: code clarity. You should always strive for descriptive code, so building your array with descriptive associative keys would make the intent of the code clearer.

Proposed solution based off the example data (rough sketch that you should tailor to your specific needs):

function readCSV($csvFile)
{
    $output = [];
    $fileHandle = fopen($csvFile, 'r');
    $header = fgetcsv($fileHandle);
    while (!feof($fileHandle)) {
        $fileRow = fgetcsv($fileHandle, 1024);
        $orderId = $fileRow[0];
        // skip this row if it's empty (the first field contains no id)
        if (empty($orderId)) {
            continue;
        }
        /*
          $fileRow[3] is "Buyer name", the first field that's present in one type of row
          (the one containing common properties of the order). By checking if it's empty,
          we identify the contents of the row - not empty means order row with common
          properties, empty means item row with specific item properties.
         */
        if (!empty($fileRow[3])) {
            // no need to repeat the id inside the array - it's already stored in the key
            $output[$orderId] = [
                'order_number' => $fileRow[1],
                'buyer_username' => $fileRow[2],
                'buyer_name' => $fileRow[3],
                // here you can continue explicitly adding any property you need
            ];
        } else {
            // add a new item entry
            $output[$orderId]['items'][] = [
                'item_number' => $fileRow[20],
                'item_title' => $fileRow[21],
                'quantity' => $fileRow[24],
                'price' => $fileRow[25],
                // here you can continue explicitly adding any property you need
            ];
        }
    }
    fclose($fileHandle);

    return $output;
}

Now all the items of your order are neatly stored as subarrays, each containing only data specific to that item, which makes it really easy to iterate:

foreach($orders[$orderId]['items'] as $item)
Wednesday, March 31, 2021
 
Magnanimity
answered 9 Months ago
23

I am not sure if this will be useful. I have noticed that the ArrayObject class is 'interesting'...

I am not sure that this is even an 'answer'. It is more an observation about this class.

It handles the 'multidimensional array' stuff correctly as standard.

You may be able to add methods to make it do more of what you wish?

<?php //

class Config extends ArrayObject
{

//    private $data = array();

    public function __construct(array $data = array())
    {
        parent::__construct($data);
    }
}

$conf = new Config(array('a' => 'foo', 'b' => 'bar', 'c' => array('sub' => 'baz')));
$conf['c']['sub'] = 'notbaz';
$conf['c']['sub2'] = 'notbaz2';

var_dump($conf, $conf['c'], $conf['c']['sub']);

unset($conf['c']['sub']);

var_dump('isset?: ', isset($conf['c']['sub']));

var_dump($conf, $conf['c'], $conf['c']['sub2']);

Output:

object(Config)[1]
  public 'a' => string 'foo' (length=3)
  public 'b' => string 'bar' (length=3)
  public 'c' => 
    array
      'sub' => string 'notbaz' (length=6)
      'sub2' => string 'notbaz2' (length=7)

array
  'sub' => string 'notbaz' (length=6)
  'sub2' => string 'notbaz2' (length=7)

string 'notbaz' (length=6)

string 'isset?: ' (length=8)

boolean false

object(Config)[1]
  public 'a' => string 'foo' (length=3)
  public 'b' => string 'bar' (length=3)
  public 'c' => 
    array
      'sub2' => string 'notbaz2' (length=7)

array
  'sub2' => string 'notbaz2' (length=7)

string 'notbaz2' (length=7)
Saturday, May 29, 2021
 
Jubair
answered 7 Months ago
83

I think you need to do that manually:

# Insert at offset 2
$offset = 2;
$newArray = array_slice($oldArray, 0, $offset, true) +
            array('texture' => 'bumpy') +
            array_slice($oldArray, $offset, NULL, true);
Thursday, June 3, 2021
 
VostanAzatyan
answered 7 Months ago
51

My suspicion is that you would have to write your own. If I understand the architecture you are describing, then you will need to send the entire chunk of data in a single piece. If so, then most libraries will not work for that because they will most likely be allocating multiple pieces of memory, which would require multiple transfers (and an inside understanding of the structure). It would be similar to trying to use a library hash function and then sending its contents over the network on a socket just by passing the root pointer to the send function.

It would be possible to write some utilities of your own that manage a very simple associative array (or hash) in a single block of memory. If the amount of data is small, it could use a simple linear search for the entries and would be a fairly compact bit of code.

Tuesday, June 22, 2021
 
antoniputra
answered 6 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :
 
Share