Home > Back-end >  Zipping two arrays in Javascript
Zipping two arrays in Javascript

Time:09-07

I have the following arrays:

const prev = [A, C, D, E]
const curr = [A, B, D, F]

What is the most elegant way of producing the following output?

[[A, A], [null, B], [C, null], [D, D], [E, null], [null, F]]

A, B, C, .. are objects that can be mapped on an equal field, e.g.:

A = {id: "A", ... }

The order of the final array doesn't matter.

EDIT: The two arrays are not necessarily the same length.

CodePudding user response:

Here is what I think is a solution that is easy to understand (but may not be the fastest or shortest).

  • First, combine the arrays (concat)
  • While there are elements in the new list:
    • Get the first element
    • Use the given comparator to find the index of another element deemed the same by the comparator
    • If an index was found, use the index to get the element and delete it from the list
      • otherwise, it's null
    • Add the first element and the other element to the result

It seems to work, and you can change the comparator if you want to change the criteria the output is zipped by.

const prev = ["A", "C", "D", "E"];
const curr = ["A", "B", "D", "F"];

const comparator = (a, b) => a === b; // a.id === b.id

const zip = (a, b, comparator) => {
    const result = [];
    
    const list = a.concat(b);
    
    while (list.length) {
        const element = list.shift();
        
        const pairIndex = list.findIndex((x) => comparator(x, element));
        
        const pair = pairIndex >= 0 ? list.splice(pairIndex, 1)[0] : null;
        
        result.push([element, pair]);
    }
    
    return result;
};

console.log(zip(prev, curr, comparator));

CodePudding user response:

I would use this algorithm

const prev = [{ id: "A" }, { id: "C" }, { id: "D" }, { id: "E" }]
const curr = [{ id: "A" }, { id: "B" }, { id: "D" }, { id: "F" }]

const res = [];

const charCodeA = 'A'.charCodeAt(0);

let prevIndex = 0, currIndex = 0;

while (prevIndex < prev.length || currIndex < curr.length) {
  const left = prev[prevIndex]?.id.charCodeAt(0) === res.length   charCodeA ? 
    prev[prevIndex] : null;
  const right = curr[currIndex]?.id.charCodeAt(0) === res.length   charCodeA ?
    curr[currIndex] : null;
 
  
  if (left) {
    prevIndex  ;
  }
  
  if (right) {
    currIndex  ;
  }
  
  res.push([left, right])
}

console.log(res)

CodePudding user response:

Use set operations to find all the values that are in common between the two arrays, only in the first array, and only in the second array. Then create the appropriate pairs for each group.

const prev = ['A', 'C', 'D', 'E'];
const curr = ['A', 'B', 'D', 'F'];

const prevSet = new Set(prev);
const currSet = new Set(curr);

const intersection = prev.filter(el => currSet.has(el));
const only_prev = prev.filter(el => !currSet.has(el));
const only_curr = curr.filter(el => !prevSet.has(el));

let result = intersection.map(el => [el, el]);
result = result.concat(only_prev.map(el => [el, null]));
result = result.concat(only_curr.map(el => [null, el]));

console.log(result);

CodePudding user response:

Iterate the first one and populate the hash value => [value,null]. Iterate the second one, if value is in the hash, replace null with the value, otherwise add [null, value]:

const prev = ['A','C','D','E']
const curr = ['A','B','D','F']

let m = {}

for (let a of prev)
    m[a] = [a, null]

for (let a of curr)
    if (a in m)
        m[a][1] = a
    else
        m[a] = [null, a]

console.log(...Object.values(m))

CodePudding user response:

Maybe try something more generic like this:

function zip(lists = []) {
    const joined = lists.flatMap(list => list); // Put all elements in a single list
    const set = new Set(joined); // Create a set to only have distinct elements
    const items = [...set]; // Convert the set to a list
    items.sort() // Sort the list
    const result = []; // Create the result array
    items.forEach(item => {
        const zippedItem = []; // Create the zip line
        lists.forEach(list => {
            if (list.includes(item)) zippedItem.push(item); // only push the item if is present in one of the original arrays
            else zippedItem.push(null); // Otherwise push a null value
        });
        result.push(zippedItem); // Push the zipped item to the result array
    })
    return result; // Return the zipped values
}

Use the function like this:

const prev = ['A', 'C', 'D', 'E'];
const curr = ['A', 'B', 'D', 'F'];

const zipped = zip([prev, curr]);

This will produce this result;

[
  [ 'A', 'A' ],
  [ null, 'B' ],
  [ 'C', null ],
  [ 'D', 'D' ],
  [ 'E', null ],
  [ null, 'F' ]
]
  • Related