How to split array of arrays into smaller parts - javascript

I have something like this, an array of arrays where each array has a 7 values
const arrays = [[1,2,3,4,5,6,7],[8,9,10,11,12,13,14]]
What's the most efficient way to split this into array of arrays where each array will have a 2 values and last array of arrays will hold the remaining 1 element, like this
[[1,2], [8,9]] [[3,4], [10,11]] [[5,6], [12,13]] [[7], [14]]

You can use array#reduce and array#forEach to split your array based on size.
const arr = [[1,2,3,4,5,6,7],[8,9,10,11,12,13,14]],
size = 2,
result = arr.reduce((r, a, i) => {
a.forEach((v,j) => {
const idx = Math.floor(j/size);
r[idx] = r[idx] || [];
r[idx][i] = r[idx][i] || [];
r[idx][i].push(v);
});
return r;
},[]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

It doesn't look that efficient, but it works.
const arrays = [[1,2,3,4,5,6,7],[8,9,10,11,12,13,14]];
// number of sub arrays
var numsubarr = Math.ceil(arrays[0].length / 2);
// parring up the vaules
var arr1 = arrays.map(arr => Array.from(Array(numsubarr).keys()).map(i => arr.slice(i*2, i*2+2)));
// combining the two arrays
var arr2 = Array.from(Array(numsubarr).keys()).map(i => [arr1[0][i],arr1[1][i]]);
console.log(arr2);

Related

Given a list of indices, combine columns

say I have a list of indices, where no index is greater than N-1:
const indices = [0,4,8,22];
and given a matrix A of MxN, is there shorthand to combine the columns into a smaller matrix, this is how I would do it:
const matrixSubset = indices.reduce((a,b) => {
a[b] = A[b];
return a;
},[]);
to do it without iteration, would be:
const a = [A[0],A[4],A[8],A[22]];
This will probably be as simple/concise as it gets:
const a = indices.map(i => A[i])

Find Missing levels and fill it

I have two arrays and need to fill the missing values with NA by comparing the levels present in other array. I used the arr.find to search but not sure how to proceed further.
Input:
const levels = [1,2,3,4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
Output:
out = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":2,"NAME1":"NA"},{"LEVEL":3,"NAME1":"TOM"},{"LEVEL":4,"NAME1":"NA"}]
Code:
let presentLevels = [];
for (let i = 1; i <= levels.length; i++) {
let check = arr.find(p => p['LEVEL'] === levels[i])
if (check) {
presentLevels.push(i)
}
}
console.log(presentLevels)
You can use map() the levels array. Find the object with LEVEL equal to the current element. If you find an object then just return that otherwise return a new object with LEVEL and NAME1 props
const levels = [1,2,3,4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
const res = levels.map(x => (arr.find(a => a.LEVEL === x) || {level: x, NAME1: "NA"}));
console.log(res)
Using Array.find() inside a loop might cause a performance issue if the arr is large enough. I would create a Map of existing levels by LEVEL, and then use the Map to get the existing levels.
Since you want the presentLevels array to be ordered by the number of the level, you'll need to iterate the levels array, and return a new array. You can do this easily with Array.map(). On each iteration take the current level from the existing Map, and if not found in existing return a new object with NA.
const levels = [1, 2, 3, 4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
const existingMap = new Map(arr.map(o => [o.LEVEL, o]))
const presentLevels = levels.map(LEVEL =>
existingMap.get(LEVEL) || { LEVEL, NAME1: 'NA' }
);
console.log(presentLevels)
You can make a loop with levels to get the items which arr doesn't have, then adding that items to arr
const levels = [1,2,3,4]
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}]
var items = levels.filter(level => !arr.find(item => item.LEVEL === level));
items.forEach(level => arr.push({LEVEL: level, NAME1: "NA"}));
console.log(arr.sort((a, b) => a.LEVEL - b.LEVEL));
You could first map the levels to the object array with all NA, and then iterate arr to replace those where necessary:
const levels = [1,2,3,4];
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}];
const result = levels.map(LEVEL => ({LEVEL, NAME1: "NA"}) );
for (let o of arr) result[o.LEVEL-1] = o;
console.log(result);
Although this executes two loops, they are not nested, and so this task is performed with linear time complexity (contrary to solutions that have a find call inside the loop).
maybe like this:
const levels = [1,2,3,4];
const arr = [{"LEVEL":1,"NAME1":"JACK"},{"LEVEL":3,"NAME1":"TOM"}];
for(var key_l in levels){
var found_levels = false;
for(var key_ar in arr){
if(arr[key_ar].LEVEL == levels[key_l]){
found_levels = true;
}
}
if(!found_levels){
arr.push({"LEVEL":levels[key_l],"NAME1":"NA"});
}
}
/* for result sorting, if need... */
arr.sort(function(a, b){
return a.LEVEL > b.LEVEL;
});
console.log(arr);

Return unique elements from two arrays

I have two arrays of numbers I want get get the unique numbers that appears in both arrays. Then I want to also return the unique numbers from both arrays.
For example:
INPUT:
let arr1 = [1234,4056,3045]
let arr2 = [5678,1234,5001]
OUTPUT:
only in arr1: [4056, 3045]
only in arr2: [5678, 5001]
in both lists: [1234]
Here is my solution, it works but I can't think of how optimize my solution. Just using JavaScript, no tools like loadash. Any thoughts?:
const getUniqueNumbers = (arr1, arr2) => {
let uniqueOfBoth = arr1.filter((ele) => {
return arr2.indexOf(ele) !== -1
})
let uniqueOfList1 = arr1.filter((ele) => {
return arr2.indexOf(ele) == -1
})
let uniqueOfList2 = arr2.filter((ele) => {
return arr1.indexOf(ele) == -1
})
return `Unique numbers from both list are ${uniqueOfBoth}
Unique nums to List1 : ${uniqueOfList1}
Unique nums to List2 : ${uniqueOfList2}
`
}
let result = getUniqueNumbers([1234, 4056, 3045], [5678, 1234, 5001])
console.log(result)
I think this approach is fine so long as it doesn't become a bottle neck. You are doing three O(n**2) operations to get your lists, so it could be nice if there was a way to reduce the complexity.
One thing you could try is to use a hash table that keeps count of how many times the numbers are seen. But you need to be a little clever because you can't just count otherwise you wouldn't know if 1 means arr1 or arr2. But since there are only 4 possibilities you only need 2 bits to represent them. So you add 1 when it's in array1 and 2 when it's in array1. That means 1 in is arr1, 2 in arr2, and 3 is in both. Creating the counts is only O(n+m) where n and m are the array lengths. (You still need to filter that, however, to get your final result)
const getUniqueNumbers =(arr1,arr2) =>{
let counter = {}
arr1.forEach(i => counter[i] = counter[i] ? counter[i] + 1 : 1)
arr2.forEach(i => counter[i] = counter[i] ? counter[i] + 2 : 2)
return counter
}
let counts = getUniqueNumbers([1234,4056,3045],[5678,1234,5001])
console.log(counts)
Then it's just a matter of filtering what you want with something like:
let both = Object.keys(counter).filter(key => result[key] === 3)
You could use Array#includes instead of Array#indexOf, because it returns a boolean value instead of the index.
For getting a difference, you could filter by the unique values of both arrays (this yields a smaller set, than to take the original arrays).
const getUniqueNumbers = (arr1, arr2) => {
let uniqueOfBoth = arr1.filter(ele => arr2.includes(ele))
let uniqueOfList1 = arr1.filter((ele) => !uniqueOfBoth.includes(ele))
let uniqueOfList2 = arr2.filter((ele) => !uniqueOfBoth.includes(ele))
return `Unique numbers from both list are ${uniqueOfBoth}\nUnique nums to List1 : ${uniqueOfList1}\nUnique nums to List2 : ${uniqueOfList2}`
}
let result = getUniqueNumbers([1234, 4056, 3045], [5678, 1234, 5001])
console.log(result)
Here's another version.
This solution assumes the arrays are of equal length. We first iterate through the arrays and store the values in 2 dictionaries. This eliminates any duplicate integers found in the same array. We then iterate over one of the dictionaries and check if the key is found in both, then delete that key from both. Finally, we get the remaining keys from both dictionaries and store them as arrays.
const fn = (arr1, arr2) => {
const obj = {
arr1: [],
arr2: [],
both: []
};
const dict1 = {};
const dict2 = {};
for (let i = arr1.length; i--;) {
dict1[arr1[i]] = true;
dict2[arr2[i]] = true;
}
for (let key in dict1) {
if (key in dict2) {
obj.both.push(key);
delete dict1[key];
delete dict2[key];
}
}
obj.arr1 = Object.keys(dict1);
obj.arr2 = Object.keys(dict2);
return obj;
}
const arr1 = [1234, 4056, 3045];
const arr2 = [5678, 1234, 5001];
console.log(fn(arr1, arr2));

Identical values in an array has to be positioned together

var array1 = ["ddd","aaa","eee","aaa","fff","bbb","ggg","aaa","ccc","fff"]
i have to arrange it in a way that identical values has to be placed together
output array should be
["ddd","aaa","aaa","aaa","eee","fff","fff","bbb","ggg","ccc"]
how to write the logic for this one ??
You could iterate the array and check the position and if not the same as the actual element, splice the value to the earlier position.
var array = ["ddd","aaa","eee","aaa","fff","bbb","ggg","aaa","ccc","fff"],
i = 0, p;
while (i < array.length) {
p = array.indexOf(array[i]);
if (p !== i) {
array.splice(p, 0, array.splice(i, 1)[0]);
}
i++;
}
console.log(array);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Sort array using sort() method like this
var array1 = ["ddd","aaa","eee","aaa","fff","bbb","ggg","aaa","ccc","fff"];
console.log(array1.sort());
Well actually for you request .sort() might be an overkill. You can simply do this by;
function quasiSort(a){
var h = a.reduce((h,e) => h[e] ? (h[e].push(e), h)
: (h[e] = [e], h), {});
return Object.keys(h).reduce((r,k) => r.concat(h[k]),[]);
}
var array1 = ["ddd","aaa","eee","aaa","fff","bbb","ggg","aaa","ccc","fff"];
console.log(quasiSort(array1));
You can create one object to store values and index and then use that object to create new array.
var array1 = ["ddd","aaa","eee","aaa","fff","bbb","ggg","aaa","ccc","fff"],
obj = {}, i = 0
array1.forEach(function(e) {
if(!obj[e]) obj[e] = {index: i++, values: []}
obj[e].values.push(e)
})
var result = [].concat(...Object.keys(obj).reduce((r, e) => (r[obj[e].index] = obj[e].values, r), []))
console.log(result)

How do I join an array with the result not being a string in Javascript? [duplicate]

This question already has answers here:
Join sequence of arrays with array delimeters ("intersperse")
(7 answers)
Closed 5 years ago.
I have an array [1,2,3,4,5,6] and a separator '~' and I want to joint them into a new array with '~' being the separator.
I'd like the output to be [1,'~', 2,'~', 3,'~', 4,'~', 5,'~', 6].
Using Lodash I got something like:
var my_array = [1,2,3,4,5,6]
var separator = '~'
_.flatten(_.zip(my_array, new Array(my_array.length).fill(separator)))
But this feels ugly and I'm sure there is a better way.
EDIT: Even though the array above has ints I'd like this to work for any type of object.
Why not in pue Javascript:
Minor Update: to account for values greater then 9
first join it to a string my_array.join("~")
then split every char .split(/\b/gi)
var my_array = [1,2,3,4,5,6,10,11]
var separator = '~'
console.info(my_array.join("~").split(/\b/gi));
Update (even if closed):
In Regard to point, other Objects. This should work, even if not a one-liner.
var myArray = [1,2,3,45,6,10,new Date()];
var newArray = myArray.reduce((p,n)=>{
if(p.length){
p.push("~");
}
p.push(n);
return p;
},[]);
console.info(newArray)
Nice simple forEach without a dozen temporary arrays, etc.:
var my_array = [1,2,3,4,5,6];
var result = [my_array[0]];
my_array.forEach(function(entry, index) {
if (index > 0) {
result.push("~", entry);
}
});
console.log(result);
Or you can get rid of the if with a single temporary array:
var my_array = [1,2,3,4,5,6];
var result = [my_array[0]];
my_array.slice(1).forEach(function(entry, index) {
result.push("~", entry);
});
console.log(result);
Just throwing my hat in:
arr.map(x => [x, '~']).reduce((p, c) => p.concat(c));
This isn't very hacky, it maps every element into two elements and concats them together, it is pretty easy to generalize:
const intercalate = (arr, sep) => arr.map(x => [x, sep])
.reduce((p, c) => p.concat(c))
.slice(0, -1);
Or with a single reduce:
const intercalate = (arr, sep) => arr.reduce((p, c) => p.concat(c, sep)).slice(0, -1);
Here is an option using forEach -
var a = [1,2,3]
var sep = '~'
var b = []
a.forEach(function(x) { b.push(x, sep) })
b.pop() // remove the last `~`
Using _.flatMap,
var my_array = [1,2,3,4,5,6];
var separator = '~';
console.log(_.flatMap(my_array, function( v ){ return [v,separator] }).slice(0,-1));
Update:
Ensure trailing ~ is removed.
Using a traditional for loop
var nums = [1,2,3,4,5,6];
var dash = '~';
var res = [];
for (var i=0; i<nums.length; i++){
res.push(nums[i]);
res.push(dash);
}
res.pop();
console.log(res);
You could use Array#reduce and concat a tilde if necessary.
var array = [1, 2, 3, 4, 5, 6],
result = array.reduce(function (r, a, i) {
return r.concat(i ? '~' : [], a);
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Anosther proposal with Array#forEach
var array = [1, 2, 3, 4, 5, 6],
result = [];
array.forEach(function (a) {
result.push(a, '~');
});
result.length--;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories

Resources