Emm
Emm

Reputation: 2507

Mapping arrays into object with values as an array

I would like to create an object containing all the values from the array first and the values from second as an array of values, assuming that this key references multiple numbers such that given the arrays:

first = [3,0,5,3]
second = [1,3,10,5]

my output would be {0: 3, 3: [1,5], 5: 10}

This is what I have tried:

const newObj = {}
for(let i = 0; i < first.length; i++){
    newObj[first[i]] =  second[i] ? [ second[i] ] : second[i]
}

This is what I get:

{ '0': [ 3 ], '3': [ 5 ], '5': [ 10 ] }

Upvotes: 0

Views: 78

Answers (4)

Rodrigo Rodrigues
Rodrigo Rodrigues

Reputation: 8536

This is my suggestion for it, using Array.reduce, spread syntax and computed property names

first.reduce((o, k, i) => ({ ...o, [k]: [...o[k]||[], second[i]] }), {})

first = [3,0,5,3]
second = [1,3,10,5]
output = first.reduce((o, k, i) => ({ ...o, [k]: [...o[k]||[], second[i]] }), {})

console.log(output)

You can find detailed explanation on this strategy in this other answer.

Upvotes: 0

jsejcksn
jsejcksn

Reputation: 33691

I would collect all the values into arrays, regardless of how many there are (the transform function below does this) — having a data structure with uniform types is more predictable/easier to work with.

Then, in a subsequent step, the single element from the arrays with only one element can be extracted (the extractSingleArrayElements function below does this). See comments in the code for explanation:

Code in TypeScript Playground

'use strict';

/**
 * Takes an array of keys and an array of values, and returns an object
 * with values collected in arrays grouped by the keys that match their indexes
 *
 * @param {number[]} keys Array of integer keys
 * @param {number[]} values Array of integer values
 * @returns {Record<string, number[]>}
 */
function transform (keys, values) {
  // Ensure that the lengths of the arrays are the same before proceeding:
  if (keys.length !== values.length) throw new Error('Input lengths differ');
  const result = {};

  for (let i = 0; i < values.length; i += 1) {
    const key = keys[i];
    const value = values[i];
    // Create a refrence to the array of values at the key,
    // first creating and assigning it to the object at the key
    // if it doesn't already exist:
    const array = (result[key] ??= []);
    array.push(value);
  }

  return result;
}

/**
 * Takes an object with array values and returns a shallow copy of the object,
 * replacing any array values that have only one element with the element itself
 *
 * @param {Record<string, number[]>} obj
 * @returns {Record<string, number | number[]>}
 */
function extractSingleArrayElements (obj) {
  const result = {};
  for (const [key, array] of Object.entries(obj)) {
    result[key] = array.length === 1 ? array[0] : array;
  }
  return result;
}

const keys = [3, 0, 5, 3];
const values = [1, 3, 10, 5];

const transformed = transform(keys, values);

const actual = extractSingleArrayElements(transformed);

const expected = {0: 3, 3: [1, 5], 5: 10};

console.log(JSON.stringify(actual) === JSON.stringify(expected)); // true

Upvotes: 0

Tushar Shahi
Tushar Shahi

Reputation: 20376

You can loop through all the items. You have three conditions for every array element you get. You will try to map it to a key of your new object you are creating and these will be the cases:

  1. the key exists and value is already an array. (just push to the value)
  2. the key exists and is not an array. (just create an array of two values)
  3. the key does not exist. (just add a new key value pair)

const first = [3,0,5,3];
const second = [1,3,10,5];
const ans = {};
first.forEach((curr,index) => {
  if(Array.isArray(ans[curr])){ ans[curr] = [...ans[curr],second[index]];
  }
  else{
   if(ans[curr]){ans[curr] = [ans[curr],second[index]];
   }
   else ans[curr] = second[index];
  }
});

console.log(ans);

Upvotes: 0

tenshi
tenshi

Reputation: 26307

This is the easiest method to understand as it's just the bare logic written in its entirety.

for (let i = 0; i < first.length; i++) {
    if (first[i] in newObj) { // If key exists already
        if (typeof newObj[first[i]] === "number") { // and if it's a number
            newObj[first[i]] = [newObj[first[i]]]; // then we wrap it into an array
        }

        newObj[first[i]].push(second[i]); // add the new number
    } else {
        newObj[first[i]] = second[i]; // if it doesn't exist then add it
    }
}

Upvotes: 1

Related Questions