This question already has answers here:
Merge two arrays with alternating values
(14 answers)
Closed 4 years ago.
I found this question but it is closed, author narrowed it to jQuery, and answers are only for case when two arrays has equal size.
So my question is how to merge two arbitrary arrays where elements alternate? (in answer provide function m(a,b) which take two arrays a and b and return merged array)
Test cases:
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
var m = (a,b) => "...magic_here...";
m(as,b); // -> [1,"a",2,"b",3,"c","d","e"]
m(am,b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
m(al,b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
You can travel all elements and add it to result.
const as = [1, 2, 3];
const am = [1, 2, 3, 4, 5];
const al = [1, 2, 3, 4, 5, 6, 7];
const b = ["a", "b", "c", "d", "e"];
function m(a, b) {
const l = Math.max(a.length, b.length);
const result = [];
for (let i = 0; i < l; i++) {
if (a[i] !== undefined) {
result.push(a[i]);
}
if (b[i] !== undefined) {
result.push(b[i]);
}
}
console.log(result);
return result;
}
m(as, b); // -> [1,"a",2,"b",3,"c","d","e"]
m(am, b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
m(al, b); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
A very simple is to loop over and check if value exists. If yes, push else continue.
Solution 1
function alternateMerge(a1, a2) {
var length = Math.max(a1.length, a2.length);
var output = [];
for(var i = 0; i< length; i++) {
if (!!a1[i]) {
output.push(a1[i])
}
if (!!a2[i]) {
output.push(a2[i])
}
}
return output;
}
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
console.log(alternateMerge(as, b).join())
console.log(alternateMerge(am, b).join())
console.log(alternateMerge(al, b).join())
Solution 2
function alternateMerge(a1, a2) {
const arr = a1.length > a2.length ? a1 : a2;
return arr.reduce((acc, _, i) => {
!!a1[i] && acc.push(a1[i]);
!!a2[i] && acc.push(a2[i]);
return acc;
}, [])
}
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
console.log(alternateMerge(as, b).join())
console.log(alternateMerge(am, b).join())
console.log(alternateMerge(al, b).join())
You can use array#concat with spread syntax to generate the array merged alternatively.
var m = (a,b) => {
const minLen = Math.min(a.length, b.length);
return [].concat(...a.slice(0, minLen).map((v,i) => [v, b[i]]), a.slice(minLen, a.length), b.slice(minLen, b.length));
};
var as = [1,2,3];
var am = [1,2,3,4,5];
var al = [1,2,3,4,5,6,7];
var b = ["a","b","c","d","e"];
console.log(m(as,b)); // -> [1,"a",2,"b",3,"c","d","e"]
console.log(m(am,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
console.log(m(al,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
You can do:
const as = [1,2,3];
const am = [1,2,3,4,5];
const al = [1,2,3,4,5,6,7];
const b = ["a","b","c","d","e"];
const m = (a, b) => (a.length > b.length ? a : b)
.reduce((acc, cur, i) => a[i] && b[i] ? [...acc, a[i], b[i]] : [...acc, cur], []);
console.log(m(as,b)); // -> [1,"a",2,"b",3,"c","d","e"]
console.log(m(am,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e"]
console.log(m(al,b)); // -> [1,"a",2,"b",3,"c",4,"d",5,"e",6,7]
.as-console-wrapper { max-height: 100% !important; top: 0; }
Related
What's the best way to mix multiple arrays like the way in the image below,
PS:
I don't know what will be the length of each array
Arrays will contain +10000 elements
There will be more than 3 arrays
I made a solution for it but I'm looking for any better solution
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
You could use lodash for your solution.
const { flow, zip, flatten, filter} = _
const doTheMagic = flow(
zip,
flatten,
filter
)
const one = [1, 2, 3]
const two = ['😕', '🤯', '🙈', '🙊', '🙉', '😃']
const three = ['foo', 'bar', 'wow', 'okay']
const result = doTheMagic(one, two, three)
console.log(result)
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.15/lodash.min.js"></script>
Works with different length of arrays and makes use of functional programming for elegant code.
Here's a codepen to run: https://codepen.io/matteodem/pen/mddQrwe
Here's my Own solution, I was looking for any better idea
import { compact, flattenDeep } from "lodash/array";
export const doTheMagic = master => {
const longest = master.reduce((p, c, i, a) => (a[p].length > c.length ? p : i), 0);
const mixed = master[longest].map((i, k) => {
return master.map((o, a) => {
if (master[a][k]) return master[a][k];
});
});
const flaten = flattenDeep(mixed);
return compact(flaten);// to remove falsey values
};
const one = [1,2,3];
const two = ['a','b','c','d','e'];
const three = ['k','l','m','n'];
const mix = doTheMagic([one,two,three]);
console.log('mix: ', mix);
let a1 = [1, 2, 3, 4, 5];
let a2 = ["🏯", "🏜", "🏭", "🎢", "🌠", "🏗"];
let a3 = ['one', 'two', 'three', 'four', 'five'];
const doTheMagic = arrayOfArrays => {
let maxLength = 0;
let result = [];
for (arr in arrayOfArrays) {
maxLength = Math.max(maxLength, arrayOfArrays[arr].length);
}
for (let i = 0; i < maxLength; i++) {
for (let j = 0; j < arrayOfArrays.length; j++) {
if (arrayOfArrays[j][i]) {
result.push(arrayOfArrays[j][i]);
}
}
}
return result;
}
console.log(doTheMagic([a1, a2, a3]));
This works with an unknown number of arrays, each one of unknown length :
const arrays = [
[1, 2, 3, 4],
["a", "b", "c", "d", "e"],
["#", "#", "?"]
];
let output = [];
while (arrays.some(a => a.length)) { // While any of the arrays still has an element in it, keep going
for (let a of arrays) {
if (!a.length) continue;
output.push(a.shift()); // remove the first element of the array and add it to output
}
}
console.log(output)
This is my approach to achieve that, one for loop can make it. This will work if you don't know the number of arrays and array length as well.
function doTheMagic(arr){
let ans = [];
let maxLength = -1;
arr.forEach((tempArr)=>{
if(tempArr.length > maxLength){
maxLength = tempArr.length;
}
})
let l1=0,l2=0,l3=0;
for(let i=0;i<maxLength;i++){
arr.forEach((tempArr)=>{
if(tempArr[i]){
ans.push(tempArr[i]);
}
})
}
return ans;
}
let a1 = [1,2,3,4,5];
let a2 = ["🏯","🏜","🏭","🎢","🌠","🏗"];
let a3 = ['1','2','3','4','5'];
console.log(doTheMagic([a1,a2,a3]));
Not sure if this is the better, but how you can write code that handles any number of arrays passed in.
const weave = (...args) => // convert arguments to an array
args.reduce((res, arr, offset) => { // loop over the arrays
arr.forEach((v, i) => res[offset + i * args.length] = v) // loop over array and add items to their indexes
return res
}, []).filter(x => x !== undefined) // remove the unused indexes
const one = [1, 2, 3]
const two = ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
const three = ['w', 'x', 'y', 'z']
const result = weave(one, two, three)
console.log(result)
const result2 = weave(one, two)
console.log(result2)
const result3 = weave(one, two, three, ['*', '&'])
console.log(result3)
I have a function for search the longest common elements in two array:
/**
* Return the common elements in two array
*/
function searchArrayInArray(array1, array2) {
var result = [];
for (var j = 0, e = array1.length; j < e; j++){
var element = array1[j];
if( array2.indexOf(element) !== -1 ){
result.push(element);
}
}
return result;
}
This method works, but I want improve performance because I call it many times.
There is any performance improvement appliable?
Side note: the elements into the arrays are unsorted string
/**
* Return the common elements in two array
*/
function searchArrayInArray(array1, array2) {
var result = [];
for (var j = 0, e = array1.length; j < e; j++){
var element = array1[j];
if( array2.indexOf(element) !== -1 ){
result.push(element);
}
}
return result;
}
var result = searchArrayInArray(['a', 'b'], ['b', 'c']);
document.getElementById("result").innerHTML = JSON.stringify(result, null, 2);
<pre id="result"></pre>
If you're concerned about performance, you'll want to use a data structure which provides good look-up times. Array methods like Array.prototype.indexOf, Array.prototype.includes, and Array.prototype.find all have linear look-ups. Map has binary look-up and Set has constant look-up. I think Set will be ideal in this situation.
A straightforward implementation of intersection -
const intersection = (a1 = [], a2 = []) =>
{ const s =
new Set(a1)
const result =
[]
for (const x of a2)
if (s.has(x))
result.push(x)
return result
}
console.log(intersection(['a', 'b'], ['b', 'c']))
// [ 'b' ]
This can be simplified a bit using higher-order functions like Array.prototype.filter -
const intersection = (a1 = [], a2 = []) =>
{ const s =
new Set(a1)
return a2.filter(x => s.has(x))
}
console.log(intersection(['a', 'b'], ['b', 'c']))
// [ 'b' ]
This concept can be expanded upon to support intersecting an arbitrary number of arrays -
const intersection = (a1 = [], a2 = []) =>
{ const s =
new Set(a1)
return a2.filter(x => s.has(x))
}
const intersectAll = (a = [], ...more) =>
more.reduce(intersection, a)
console.log(intersectAll(['a', 'b'], ['b', 'c'], ['b', 'd'], ['e', 'b']))
// [ 'b' ]
Well indexOf() is O(n) so by using Set() instead you can improve complexity from O(n^2) to O(n * log n)
function searchArrayInArray(array1, array2) {
var result = [];
let set = new Set();
for(el of array2){
set.add(el);
}
for (var j = 0, e = array1.length; j < e; j++){
var element = array1[j];
if( set.has(element) ){
result.push(element);
}
}
return result;
}
The easiest way:
var a = [1,2,3,4,5,6,7,8,9,10];
var b = [2,4,5,7,11,15];
var c = a.filter(value => b.includes(value))
console.log(c)
What is the shorthand and best way to find intersection?
f = ["A","B","C","D","E","F"]; //might be less than 8
b = [1,0,0,1,0,0,0,0]; //always 8 elements
Desired resulting array ["A","D"]
You could use Array#filter
var f = ["A", "B", "C", "D", "E", "F"],
b = [1, 0, 0, 1, 0, 0, 0, 0],
r = f.filter((_, i) => b[i]);
console.log(r);
Assuming your f array is never longer than your b array
f.filter((item, index) => b[index] === 1);
If you're wanting this completely shorthand you can rename item and index and drop the === 1:
f.filter((a, i) => b[i]);
var f = ["A","B","C","D","E","F"]; //might be less than 8
var b = [1,0,0,1,0,0,0,0]; //always 8 elements
console.log(f.filter((a, i) => b[i]));
var f = ["A","B","C","D","E","F"];
var b = [1,0,0,1,0,0,0,0];
var res = f.filter(function(e, i) {
return b[i]; // short for return b[i] === 1;
});
console.log(res);
Or even shorter using arrow functions like this:
var f = ["A","B","C","D","E","F"];
var b = [1,0,0,1,0,0,0,0];
var res = f.filter((e, i) => b[i]);
console.log(res);
Another Way :
$(function(){
f = ["A","B","C","D","E","F"];
b = [1,0,0,1,0,0,0,0];
x = [];
$.each(b,function(key, value){
value?x.push(f[key]):'';
});
console.log(x)
});
Since for loops are faster than filter method i suggest this:
var results = [];
for(var i=0;i<b.length;i++){
if (b[i]) results.push(f[i]);
}
Given an array of paired integers, HOW can I group by intersections. Does anyone have a simple function that could convert my input, into the desired output?
Input
var in = ["0:3", "1:3", "4:5", "5:6", "6:8"]
Desired output
[
[0, 1, 3],
[4, 5, 6, 8]
]
UPDATE:
#apsiller asked my question in the comments more clearly then I originally posted:
"Considering each number as a node in a graph, and each pairing x:y as an edge between nodes x and y, find the sets of numbers that can be traveled to using the edges defined. That is, in graph theory terms, find the distinct connected components within such a graph.
For instance, there is no way to travel from 4 to 0 so they are in different groups, but there is a way to travel from 1 to 0 (by way of 3) so they are in the same group."
To reiterate the desired output is a grouping of transversable nodes, based on a potentially random input set.
Thanks everyone. Given everyones input I was able to find a similar question on here that led me my answer. Finding All Connected Components of an Undirected Graph
The first step was to change my input to groups of pairs.
var input = [
[0, 3],
[1, 3],
[4, 5],
[5, 6],
[6, 8]
]
The next step was to use whats called Breadth-first search
function breadthFirstSearch(node, nodes, visited) {
var queue = [];
var group = [];
var pair = null;
queue.push(node);
while (queue.length > 0) {
node = queue.shift();
if (!visited[node]) {
visited[node] = true;
group.push(node);
for (var i = 0, len = nodes.length; i < len; i++) {
pair = nodes[i];
if (pair[0] === node && !visited[pair[1]]) {
queue.push(pair[1]);
} else if (pair[1] === node && !visited[pair[0]]) {
queue.push(pair[0]);
}
}
}
}
return group;
};
function groupReachableVertices(input) {
var groups = [];
var visited = {};
for (var i = 0, len = input.length; i < len; i += 1) {
var current_pair = input[i];
var u = current_pair[0];
var v = current_pair[1];
var src = null;
if (!visited[u]) {
src = u;
} else if (!visited[v]) {
src = v;
}
if (src) {
groups.push(breadthFirstSearch(src, input, visited));
}
}
return groups;
};
Putting it all together...
var output = groupReachableVertices(input);
[
[0, 1, 3],
[4, 5, 6, 8]
]
You could do something like this.
function group(data) {
var r = [[]],c = 0,a = [0]
var d = data.map(e => e.split(':').sort((a, b) => a - b)).sort((a, b) => a[0] - b[0])
d.forEach(function(e, i) {
if (e[0] > a[a.length - 1]) {
r.push(e)
a.push(e[1])
c++
} else {
r[c] = r[c].concat(e)
a[a.length - 1] = e[1]
}
})
return r.map(e => [...new Set(e)].sort((a, b) => a - b))
}
var test1 = ["0:3", "1:3", "4:5", "5:6", "6:8"]
var test2 = ["0:3", "1:3", "4:5", "9:11", "10:12", '3:6', "7:8"]
var test3 = ["20:15", "4:0", "1:3", "5:1", "9:11", "10:12", '3:6', "8:7"]
console.log(JSON.stringify(group(test1)))
console.log(JSON.stringify(group(test2)))
console.log(JSON.stringify(group(test3)))
You could use a hash table and collect all nodes in it. It works for any values.
var data = ["0:3", "1:3", "4:5", "a:8", "5:a"],
result = data
.map(function (a) { return a.split(':'); })
.reduce(function (hash) {
return function (r, a) {
if (hash[a[0]] && hash[a[1]]) {
hash[a[0]].push.apply(hash[a[0]], r.splice(r.indexOf(hash[a[1]]), 1)[0]);
hash[a[1]] = hash[a[0]];
return r;
}
if (hash[a[0]]) {
hash[a[1]] = hash[a[0]];
hash[a[1]].push(a[1]);
return r;
}
if (hash[a[1]]) {
hash[a[0]] = hash[a[1]];
hash[a[0]].push(a[0]);
return r;
}
hash[a[0]] = a.slice();
hash[a[1]] = hash[a[0]];
return r.concat([hash[a[0]]]);
};
}(Object.create(null)), []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I guess, by using Object.values() and Set object you can simply do as follows in ES6.
function getConnectedVertices(a){
return [...new Set(Object.values(a.reduce((h,v) => (h[v[0]] ? h[v[1]] ? (h[v[0]] = h[v[0]].concat(h[v[1]]),
h[v[1]] = h[v[0]])
: (h[v[0]].push(v[1]),
h[v[1]] = h[v[0]])
: h[v[1]] ? (h[v[1]].push(v[0]),
h[v[0]] = h[v[1]])
: h[v[0]] = h[v[1]] = v,
h),{})))];
}
var input = ["0:3", "1:3", "4:5", "5:6", "6:8"].map(s => s.split(":")),
result = getConnectedVertices(input);
console.log(result);
If a=[[1,[],"f",3],[3,[4,"x"]]] and b=[1,1].
I want to read a by b like a[1][1] to get [4,"x"]. Note that b is an array which should only consist of integers.
You could also do eval('a['+b.join('],[')+']') but requires the actual variable name as string and it's ugly.
Here are some of my functions:
Array.prototype.readByArray = function(a) {
var c = this;
for (var i = 0; i < a.length; i++) {
c = c[a[i]];
}
return c;
};
Array.prototype.emptyByArray = function(a) {
var c = this.readByArray(a);
c.splice(0, c.length);
};
Array.prototype.concateByArray = function(a, e) {
var c = this.readByArray(a);
for (var i = 0; i < e.length; i++) {
c.push(e[i]);
}
};
Array.prototype.setByArray = function(a, e) {
this.emptyByArray(a);
this.readByArray(a).push(e);
};
This could be useful for reading a nested array in an imperative way in this example:
Array.prototype.readByArray=function(a){var c=this;for(var i=0;i<a.length;i++){c=c[a[i]];}return c;};
var a = [1,2,3,[1,2,3,[{x: 3},"test"],4],"foo","bar"]; //Your array
var b = [0]; //Reading stack
var s = '[\n'; //Output
while(b[0]<a.length){
if(Array.isArray(a.readByArray(b))){
s+=' '.repeat(b.length)+'[\n';
b.push(-1);
}else{
s+=' '.repeat(b.length)+JSON.stringify(a.readByArray(b))+'\n';
}
b[b.length-1]++;
while(b[b.length-1]>=a.readByArray(b.slice(0,-1)).length){
b.pop();
b[b.length-1]++;
s+=' '.repeat(b.length)+']\n';
}
}
console.log(s);
Is there any better way to do this? Are there native functions for this?
You could use Array#reduce for it.
You start with the whole array and return for every element of b a part of the array until all indices are used.
var a = [[1, [], "f", 3], [3, [4, "x"]]],
b = [1, 1],
result = b.reduce(function (v, i) {
return v[i];
}, a);
console.log(result);
ES6
var a = [[1, [], "f", 3], [3, [4, "x"]]],
b = [1, 1],
result = b.reduce((v, i) => v[i], a);
console.log(result);
result[0] = 42;
console.log(a);
result.splice(0, result.length, 'test');
console.log(a);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I had written a reusable generic code exactly for this purpose to get the nested object properties dynamically. Actually i was targeting objects but since in JS an array is a perfect object it also applies for arrays too. So lets see how it works in this particular case;
Object.prototype.getNestedValue = function(...a) {
return a.length > 1 ? (this[a[0]] !== void 0 && this[a[0]].getNestedValue(...a.slice(1))) : this[a[0]];
};
var a = [[1,[],"f",3],[3,[4,"x"]]],
b = [1,1],
c = a.getNestedValue(...b);
console.log(c)