4
\$\begingroup\$

Basically, I need to regroup keys by values from an object to an array

An example worth a thousand words in my case, as I don't know how to clearly explain my problem.

Here is my input object:

{
    "Data 1": 0,
    "Data 2": 1,
    "Data 3": 3,
    "Data 4": 0
}

What I should output:

[
    ["Data 1", "Data 4"], // data 1 and 4 are grouped as they both "worth" 0
    ["Data 2"], // data 2 is alone to "worth" 1
    ["Data 3"] // data 3 is alone to "worth" 3
]

First solution:

var output = [];
for (var data in input) { // my input object
    var value = input[data];
    if (output[value] == undefined) {
        output[value] = [];
    }
    output[value].push(data);
}
console.log(output);

It works well for most of the cases, except the one presented at the beginning: if I skip a value - say 0, 1, 3 - then my output will be

[
    0: ["Data 1", "Data 4"],
    1: ["Data 3"],
    3: ["Data 4"]
]

What I understood so far is that since the key 2 is missing, JavaScript somehow understand the output as an associative array.

Final solution:

From the code below, I used a "trick", making an equivalent of array_values() from PHP:

function array_values(arr) {
    var tmp = [];
    for (key in arr) {
        tmp[tmp.length] = arr[key];
    }
    return tmp;
}

var output = [];
for (var data in input) { // my input object
    var value = input[data];
    if (output[value] == undefined) {
        output[value] = [];
    }
    output[value].push(data);
}
console.log(array_values(output));

This finally works perfectly fine.

Nevertheless, I feel like I could optimize and simplify my code. I fear that a big object could be long to process since I could have to parse it 2 times. Does anyone have a guess?

\$\endgroup\$
1
  • \$\begingroup\$ It would be easier to think that JavaScript does not have arrays and only have objects that allow numeric properties. \$\endgroup\$
    – PM 77-1
    Jan 13 '15 at 19:27
3
\$\begingroup\$

I assume that the values are no longer needed in the output array and that you only want to construct the final array that you receive after the call to your array_values function.

// I extend the data with another duplicated value for a better test.
var input = { "Data 1": 0, "Data 2": 1, "Data 3": 3, "Data 4": 0, "Data 5": 3 };

// No change, our output array.
var output = [];

// We will use an additional array to create a mapping between actual values
// and the output array's indices they are stored at.
var mapping = [];

for (var data in input) {
    // Check if the value of this input data is already present in the
    // mapping's array, if it is, append the actual data to the already
    // existing array.
    //
    // NOTE: Using `mapping[input[data]] == undefined` is not a secure method
    // to check if an array index exists, since the value might be undefined
    // and that is not what we want. It is like `isset` and `array_key_exists`
    // in PHP, if you are familiar with that language.
    if (input[data] in mapping) {
        // We access the output array via the mappings array and the original
        // input arrays value and push the data.
        output[mapping[input[data]]].push(data);
        // output[0].push("Data 4");
        // output[2].push("Data 5");
    }
    // No entry exists in the mapping's array.
    else {
        // Push will simply append the data, we also inline the creation of the
        // desired array for additional data for this index.
        output.push([ data ]);
        // output[0] = [ "Data 1" ];
        // output[1] = [ "Data 2" ];
        // output[2] = [ "Data 3" ];

        // Now we need to create the actual mapping in case other data has the
        // same value. We simply create a new index and store the output
        // array's current index there (note the `-1` since `length` starts at
        // one in contrast to the indices which start at zero).
        mapping[input[data]] = output.length - 1;
        // mapping[0] = 0;
        // mapping[1] = 1;
        // mapping[3] = 2;
    }
}

console.dir(output);
\$\endgroup\$
1
  • 1
    \$\begingroup\$ You're assuming is good. I seems to work great! Thank you \$\endgroup\$
    – pistou
    Jan 13 '15 at 19:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.