How do I create a collection of arrays from a single array? - javascript

Say I have one large array like
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18]
and would like to split it into an array of n-tuples like
[[1,2], [3,4], [5,6], [7,8], [9,10], [11,12], [13,14] /*, ... */ ] // (for n=2)
Is there some easy way to achieve this? The special case n = 2 would be enough for me.

This should work:
for (var i=0; i<arr.length; i+=2) {
result.push([arr[i], arr[i+1]]);
}
Came up with this, it should work for any number of "pockets" or whatever you want to call them. It checks for undefined so it works with odd number of items:
Array.prototype.pockets = function(n) {
var result = [],
pocket = [],
i, j;
for (i=0; i<this.length; i+=n) {
pocket.length = 0;
for (j=1; j<n; j++) if (this[i+j] != null) pocket.push(this[i+j]);
result.push([this[i]].concat(pocket));
}
if (arguments.length > 1) {
return result.pockets.apply(result, [].slice.call(arguments,1));
}
return result;
};
// Usage:
var arr = [1,2,3,4,5,6,7,8,9,10,11];
arr.pockets(2); //=> [[1,2],[3,4],[5,6],[7,8],[9,10],[11]]
arr.pockets(3); //=> [[1,2,3],[4,5,6],[7,8,9],[10,11]]
// Recursive:
arr.pockets(1,3); //=> [ [[1],[2],[3]], [[4],[5],[6]], [[7],[8],[9]], [[10],[11]] ]

This can be done much simpler by using Array.slice:
function grouper(lst, size) {
var result = [], i=0, n=lst.length;
while(i < n) {
result.push(lst.slice(i, i+size));
i += size;
}
return result
}
It's also much more efficient: http://jsperf.com/grouper

For an underscore variant, you can achieve this with _.groupBy(), grouping by the index of the item:
var doubles = _.groupBy(singles, function (num, i) {
return Math.floor(i / 2);
});
Though, since _.groupBy() returns an Object, getting an Array takes some additional work:
_.mixin({
segment: function (coll, per) {
var result = [];
_.chain(coll)
.groupBy(function (item, i) { return Math.floor(i / per)})
.each(function (group, key) { result[key] = group; })
return result;
}
});
var singles = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18];
var doubles = _.segment(singles, 2);
var triples = _.segment(singles, 3);

In python this can be done with zip(*[iter(xs)]*n). Just for fun, here's a JS implementation:
Let's start with a poor man's generator (that's all we've got until ES6 spreads around):
StopIteration = {"name": "StopIteration"}
function iter(xs) {
if('next' in xs)
return xs;
var i = 0;
return {
next: function() {
if(i >= xs.length)
throw StopIteration;
return xs[i++];
}
}
}
next = function(it) { return it.next() }
zip() is trivial:
zip = function() {
var args = [].map.call(arguments, iter), chunks = [];
while(1) {
try {
chunks.push(args.map(next));
} catch(StopIteration) {
return chunks;
}
}
}
Now, to create chained pairs just pass the same iter twice to zip:
xs = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
it = iter(xs)
a = zip(it, it)
console.log(a)
// [[1,2],[3,4],[5,6],[7,8],[9,10],[11,12]]
For N-pairs an additional utility is required:
repeat = function(x, n) {
for(var a = []; n; n--)
a.push(x);
return a;
}
a = zip.apply(this, repeat(iter(xs), 5))
console.log(a)
// [[1,2,3,4,5],[6,7,8,9,10]]
Note that like in Python this strips incomplete chunks.

Related

How do i reverse an array in javaScript without .reverse()

I'm trying to reverse the contents of an array. My approach works well when the contents of the said array are of same type. but once they're of different types it just doesn't work. Constraint is that i can't use the .reverse() method and if possible, without creating a new array.
The answer in this question is close to what i want but i don't understand it.
Here is my code...
reverse.js
#!/usr/bin/node
exports.rev = function (list) {
for (let i = 0, j = (list.length - 1); i <= (list.length / 2); i++, j--) {
[list[i], list[j]] = [list[j], list[i]];
}
return (list);
};
main.js
#!/usr/bin/node
const rev = require('./reverse').rev;
console.log(rev([1, 2, 3, 4, 5]));
console.log(rev(["School", 89, { id: 12 }, "String"]));
Expected:
[5, 4, 3, 2, 1]
["String", { id: 12 }, 89, "School"]
What I got:
[5, 4, 3, 2, 1]
["String", 89, { id: 12 }, "School"]
I've found out that your code almost works. You just need to modify the condition a bit to
i < (list.length / 2) //not `<=`
function rev(list) {
for (let i = 0, j = (list.length - 1); i < (list.length / 2); i++, j--) {
[list[i], list[j]] = [list[j], list[i]];
}
return (list);
};
console.log(rev([1, 2, 3, 4, 5]));
console.log(rev(["School", 89, { id: 12 }, "String"]));
let array = [1, 2, 3, 4, 5]
let reverse = [];
for (var i = array.length - 1; i >= 0; i--){
reverse.push(array[i]);
}
You can try with a for loop like this:
let arr = ["School", 89, { id: 12 }, "String"];
let newArr = [];
for (let i = arr.length - 1; i >= 0; i--) {
newArr.push(arr[i])
}
console.log(newArr);
Expected output: ["String", [object Object] { id: 12 }, 89, "School"]
You can user a recursion to reverse
You can use Spread operator(...) along with distructure assignment for that.
function rev(arr) {
const [first, ...rest] = arr
return arr.length > 1 ? [...rev(rest), first] : arr
}
console.log(rev([1, 2, 3, 4, 5]));
console.log(rev(["School", 89, { id: 12 }, "String"]));
const [first, ...rest] = arr is a shortcut for:
const first = arr[0]
const rest = arr.slice(1)
However, ...rev(rest) will spread items of returned array and spread them. So, [...rev(rest), first] will keep the output of rev first and then push first at the end of array.
If you are comfortable with mutation of original array, try this
function rev(arr) {
return arr.length > 1 ? [arr.pop(), ...rev(arr)] : arr
}
console.log(rev([1, 2, 3, 4, 5]));
console.log(rev(["School", 89, { id: 12 }, "String"]));
Already good answers (and examples of arrays) here
My try with native methods:
const rev = (arr) => {
len = arr.length;
for (i = 1; i<= len; i++) arr.splice(len-i,0,arr.shift());
}
const array = ["School", 89, { id: 12 }, "String"];
console.log("original",array);
rev(array);
console.log("reversed",array);

Group array elements into set of n

I have an array
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
I want to group it into a set of n arrays such that first n elements in result[0] next n elements in result[1] and if any element is remaining it is discarded.
let sampleOutput = [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9, 10, 11, 12, 13]] for n = 7;
Here is my code:
function group5(arr, len) {
let result = [];
let loop=parseInt(arr.length/len)
for (let i=0; i<arr.length; i+=len) {
let x = []; let limitReached = false;
for (let j=0; j<len; j++) {
if (arr[i+j]) {
x.push(arr[i+j]);
} else {
limitReached = true;
break;
}
}
if (!limitReached) {
result.push(x);
} else {
break;
}
}
return result;
}
But I am unable to get expected result. I have tried following things.
Map function
Running i loop to arr.len
Checking arr.len % 7
Creating an array for every third element.
This question is not duplicate of Split array into chunks because I have to discard extra elements that can not be grouped into sets of n.
I have to keep the original array Immutable because I am using this on props in a child component. I need a function that does not modify the original array.
It's pretty straigthforward using Array.from
const list = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14];
function chunkMaxLength(arr, chunkSize, maxLength) {
return Array.from({length: maxLength}, () => arr.splice(0,chunkSize));
}
console.log(chunkMaxLength(list, 7, 2));
What about :
function group5(arr, len) {
let chunks = [];
let copy = arr.splice(); // Use a copy to not modifiy the original array
while(copy.length > len) {
chunks.push(copy.splice(0, len));
}
return chunks;
}
You could use a combination of reduce and filter to achieve the expected result. This example gives you a third control over length which makes the code a bit more reuseable.
let arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
const groupNumber = 7;
const groupCount = 2;
const groupArray = (group, size, length) => group.reduce((accumulator, current, index, original) =>
((index % size) == 0)
? accumulator.concat([original.slice(index, index + size)])
: accumulator, []
).filter((single, index) => index < length)
const test = groupArray(arr, groupNumber, groupCount);
console.log(test);
Step by Step
const groupArray = (group, size, length) => {
// if (index modulus size) equals 0 then concat a group of
// length 'size' as a new entry to the accumulator array and
// return it, else return the accumulator
const reducerFunc = (accumulator, current, index, original) =>
((index % size) == 0)
? accumulator.concat([original.slice(index, index + size)])
: accumulator
// if the current index is greater than the supplied length filter it out
const filterFunc = (single, index) => index < length;
// reduce and filter original group
const result = group.reduce(reducerFunc, []).filter(filterFunc)
return result;
}
Also (apart from the existing approaches) you can have a recursive approach like this
function chunks(a, size, r = [], i = 0) {
let e = i + size;
return e <= a.length ? chunks(a, size, [...r, a.slice(i, e)], e) : r;
}
function chunks(a, size, r = [], i = 0) {
let e = i + size;
return e <= a.length ? chunks(a, size, [...r, a.slice(i, e)], e) : r;
}
var arr = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];
console.log('Chunk with 3: ', chunks(arr, 3));
console.log('Chunk with 4: ', chunks(arr, 4));
console.log('Chunk with 5: ', chunks(arr, 5));
console.log('Chunk with 6: ', chunks(arr, 6));
console.log('Chunk with 7: ', chunks(arr, 7));
I able to solve the problem with this code
function groupN(n, arr) {
const res = [];
let limit = 0;
while (limit+n <= arr.length) {
res.push(arr.slice(limit, n + limit));
limit += n
}
return res
}
I usually prefer declarative solutions (map, reduce, etc), but in this case I think a for is more understandable:
function groupArray(array, num) {
const group = [];
for (let i = 0; i < array.length; i += num) {
group.push(array.slice(i, i + num));
}
return group;
}

Change order of a string using an array indexes/ Javascript

I am stuck with this challenge, any help would be great.
'Create a function that takes both a string and an array of numbers as arguments. Rearrange the letters in the string to be in the order specified by the index numbers. Return the "remixed" string.
Examples
remix("abcd", [0, 3, 1, 2]) ➞ "acdb"'
My attempt -
function remix(str, arr) {
var arr2 = [];
for (var i=0; i<str.length; i++){
arr2.splice(arr[i], 0, str[i]);
}
return arr2.join("");
}
This will solve some but not all of the tests.
EG.
("abcd", [0, 3, 1, 2]) = "acdb" but some do not.
EG.
"responsibility", [0, 6, 8, 11, 10, 7, 13, 5, 3, 2, 4, 12, 1, 9])
should be - "rtibliensyopis" mine is "rteislbpoyinsi"
You could use the value of arr[i] as target index for the actual letter.
function remix(str, arr) {
var result = [],
i;
for (i = 0; i < str.length; i++) {
result[arr[i]] = str[i];
}
return result.join('');
}
console.log(remix("abcd", [0, 3, 1, 2])); // "acdb"
console.log(remix("responsibility", [0, 6, 8, 11, 10, 7, 13, 5, 3, 2, 4, 12, 1, 9])) // "rtibliensyopis"
function myFunction(text, nums){
var arr = [];
for(var num in nums){
arr.push(text.charAt(nums[num]));
}
return arr;
};
I think this should work.
Edited answer - misinterpreted the question at first.
You can use Array.prototype.reduce to fill the positions in a new array with letters from str based on target positions from arr:
const remix = ( str, arr ) => (
arr.reduce( (acc, target, idx) => {
acc[target] = str[idx]; return acc;
}, [])
).join('');
or reduce the array of letters in str into an array:
const remix = ( str, arr ) => (
[...str].reduce( (acc, letter, idx) => {
acc[arr[idx]] = letter; return acc;
}, [])
).join('');

Finding chained, incremented values in array

I'm trying to find all values in an array that would form a chain of incremented values - all referencing back to a certain starting value. Increments can go both "up" and "down".
array = [10, 2, 3, 5, 9, 11]
Starting with the number 2 should return:
[2, 3]
Starting with the number 10 should return:
[9, 10, 11]
There are of course plenty of inefficient ways of doing this, but I'm asking this here because doing this efficiently is important for my case and I'm such a JS newbie.
You can use Array.prototype.includes() to check if a number exists in an array. If the number is before the base reference add it using unshift, if after add it using push:
var array = [10, 2, 3, 5, 9, 11];
function findChain(array, num) {
if(!array.includes(num)) {
return [];
}
const result = [num];
let before = num - 1;
let after = num + 1;
while(array.includes(before)) {
result.unshift(before--);
}
while(array.includes(after)) {
result.push(after++);
}
return result;
}
console.log('Ref 2 -', findChain(array, 2));
console.log('Ref 5 -', findChain(array, 5));
console.log('Ref 10 -', findChain(array, 10));
console.log('Ref 20 -', findChain(array, 20));
A quick solution :
var array = [10, 2, 3, 5, 9, 11, 14, 89, 12, 8];
var trouver = nombre => {
var result = [];
if (array.indexOf(nombre) !== -1) result.push(nombre);
else return result;
for(var chiffre = nombre+1; array.indexOf(chiffre) !== -1; chiffre++) result.push(chiffre);
for(var chiffre = nombre-1; array.indexOf(chiffre) !== -1; chiffre--) result.push(chiffre);
return result.sort((a,b) => a-b);
}
console.log(trouver(9)); //[ 8, 9, 10, 11, 12 ]
Another solution could be a double chained list for it.
function getValues(array, value) {
var object = Object.create(null),
result,
o;
array.forEach(function (a) {
object[a] = object[a] || { value: a, pre: object[a - 1] || null, succ: object[a + 1] || null };
if (object[a - 1]) {
object[a - 1].succ = object[a];
}
if (object[a + 1]) {
object[a + 1].pre = object[a];
}
});
o = object[value];
if (o) {
result = [];
while (o.pre) {
o = o.pre;
}
while (o.succ) {
result.push(o.value);
o = o.succ;
}
result.push(o.value);
}
return result;
}
var array = [10, 2, 3, 5, 9, 11, 14, 89, 12, 8];
console.log(getValues(array, 2));
console.log(getValues(array, 10));
console.log(getValues(array, 42));
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try this approach
Sort the array first
Iterate array items one by one, keep pushing the start-counter if current-item value is not bigger than last-item by 1, else reset the start-counter to current-index.
For Example :
var arr = [10, 2, 3, 5, 9, 11];
function getAllSequences(arr) {
arr.sort(function(a, b) {
return a - b
});
var startIndex = 0;
var endIndex = 0;
var lastItem = 0;
var chains = [];
arr.forEach(function(item, index) {
if (index > 0) {
if ((item - lastItem) > 1) {
extractChain(chains, arr, startIndex, endIndex);
startIndex = index;
} else {
endIndex = index;
if (index == arr.length - 1) {
extractChain(chains, arr, startIndex, endIndex);
}
}
}
lastItem = item;
});
return chains;
}
console.log(getAllSequences(arr));
function extractChain(chains, arr, startIndex, endIndex) {
var value = arr.slice(startIndex, endIndex + 1);
if (value.length > 0) {
chains.push(value);
}
}

Deleting both values from array if duplicate - JavaScript/jQuery

I have an array here:
var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
Now I want to remove both appearances of a duplicate. So the desired result is not:
var myArr = [1, 2, 5, 7, 8 ,9];
but
var myArr = [2, 7, 8];
Basically I know how to remove duplicates, but not in that that special way. Thats why any help would be really appreciated!
Please note: My array is filled with strings. The numbers here were only used as an example.
jsfiddle for this code:
var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
var newArr = myArr;
var h,i,j;
for(h = 0; h < myArr.length; h++) {
var curItem = myArr[h];
var foundCount = 0;
// search array for item
for(i = 0; i < myArr.length; i++) {
if (myArr[i] == myArr[h])
foundCount++;
}
if(foundCount > 1) {
// remove repeated item from new array
for(j = 0; j < newArr.length; j++) {
if(newArr[j] == curItem) {
newArr.splice(j, 1);
j--;
}
}
}
}
Here's my version
var a = [1, 1, 2, 5, 5, 7, 8, 9, 9];
function removeIfduplicate( arr ) {
var discarded = [];
var good = [];
var test;
while( test = arr.pop() ) {
if( arr.indexOf( test ) > -1 ) {
discarded.push( test );
continue;
} else if( discarded.indexOf( test ) == -1 ) {
good.push( test );
}
}
return good.reverse();
}
x = removeIfduplicate( a );
console.log( x ); //[2, 7, 8]
EDITED with better answer:
var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
function removeDuplicates(arr) {
var i, tmp;
for(i=0; i<arr.length; i++) {
tmp = arr.lastIndexOf(arr[i]);
if(tmp === i) {
//Only one of this number
} else {
//More than one
arr.splice(tmp, 1);
arr.splice(i, 1);
}
}
}
Using Hashmap
create hashmap and count occurencies
filter where hashmap.get(value) === 1 (only unique values)
const myArray = [1, 1, 2, 5, 5, 7, 8, 9, 9];
const map = new Map();
myArray.forEach(v => map.set(v, map.has(v) ? map.get(v)+1 : 1));
myArray.filter(v => map.get(v) === 1);
Old version (slower but valid too)
Heres a short version using Array.filter(). The trick is to first find all values that are NOT uniqe, and then use this array to reject all unique items in the original array.
let myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
let duplicateValues = myArr.filter((item, indx, s) => s.indexOf(item) !== indx);
myArr.filter(item => !duplicateValues.includes(item));
// => [2, 7, 8]
Wherever removing duplicates is involved, it's not a bad idea to use a set data structure.
JavaScript doesn't have a native set implementation, but the keys of an object work just as well - and in this case help because then the values can be used to keep track of how often an item appeared in the array:
function removeDuplicates(arr) {
var counts = arr.reduce(function(counts, item) {
counts[item] = (counts[item] || 0) + 1;
return counts;
}, {});
return Object.keys(counts).reduce(function(arr, item) {
if (counts[item] === 1) {
arr.push(item);
}
return arr;
}, []);
}
var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
console.log(removeDuplicates(myArr), myArr);
Check out the example on jsfiddle.
Alternately, you could not use calls to reduce(), and instead use for and for(item in counts) loops:
function removeDuplicates(arr) {
var counts = {};
for(var i=0; i<arr.length; i++) {
var item = arr[i];
counts[item] = (counts[item]||0)+1;
}
var arr = [];
for(item in counts) {
if(counts[item] === 1) {
arr.push(item);
}
}
return arr;
}
Check out the example on jsfiddle.
If it's just alphanumeric, duplicates are case-sensitive, and there can be no more than two of any element, then something like this can work:
var a = [2, 1, "a", 3, 2, "A", "b", 5, 6, 6, "B", "a"],
clean_array = $.map(a.sort(), function (v,i) {
a[i] === a[i+1] && (a[i] = a[i+1] = null);
return a[i];
});
// clean_array = [1,3,5,"A","B","b"]
In this example,we are taking two arrays as function arguments, from this we are going to print only unique values of both arrays hence deleting the values that are present in both arrays.
first i am concatenating both the arrays into one. Then I taking each array value at a time and looping over the array itself searching for its no of occurrence. if no of occurrence(i.e.,count) equal to 1 then we are pushing that element into the result array. Then we can return the result array.
function diffArray(arr1, arr2) {
var newArr = [];
var myArr=arr1.concat(arr2);
var count=0;
for(i=0;i<myArr.length;i++){
for(j=0;j<myArr.length;j++){
if(myArr[j]==myArr[i]){
count++;
}
}
if(count==1){
newArr.push(myArr[i]);
}
count=0;
}
return newArr;
}
EDIT: Here is the jspref http://jsperf.com/deleting-both-values-from-array
http://jsfiddle.net/3u7FK/1/
This is the fastest way to do it in two passes without using any fancy tricks and keeping it flexible. You first spin through and find the count of every occurance and put it into and keyvalue pair. Then spin through it again and filter out the ones where the count was greater than 1. This also has the advanatage of being able to apply other filters than just "greater than 1"; as well as the having the count of occurances if you needed that as well for something else.
This should work with strings as well instead of numbers.
http://jsfiddle.net/mvBY4/1/
var myArr = [1, 1, 2, 5, 5, 7, 8, 9, 9];
var map = new Object();
for(var i = 0; i < myArr.length; i++)
{
if(map[myArr[i]] === undefined)
{
map[myArr[i]] = 1;
}
else
{
map[myArr[i]]++;
}
}
var result = new Array();
for(var i = 0; i < myArr.length; i++)
{
if(map[myArr[i]] > 1)
{
//do nothing
}
else
{
result.push(myArr[i]);
}
}
alert(result);
You can use Set (available in IE 11+) as below
const sourceArray = [1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 8];
const duplicatesRemoved = new Set();
sourceArray.forEach(element => {
if (duplicatesRemoved.has(element)) {
duplicatesRemoved.delete(element)
} else {
duplicatesRemoved.add(element)
}
})
console.log(Array.from(duplicatesRemoved))
N.B. Arrow functions are not supported in older browsers. Use normal function syntax for that instead. However, Array.from can easily be polyfilled for older browsers.
Try it here.

Categories