Javascript - chunk arrays for custom size - javascript

I have an array like:
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
How I can reorder this array with the following rules:
myArray[0][0] to reduce size to 2 elements (values 1,2 stay, 3,4 goes to next array)
keep all values just move extra array elements to the next array, but all arrays need to keep the current number of elements except last
WHat I already try is:
function conditionalChunk(array, size, rules = {}) {
let copy = [...array],
output = [],
i = 0;
while (copy.length)
output.push( copy.splice(0, rules[i++] ?? size) )
return output
}
conditionalChunk(myArray, 3, {0:2});
but in that case, I need to put rules for all arrays in the array, I need to know a number of elements for all arrays in the array, and that's what I want to avoid.
Is there any elegant way to do that?

The requirements were not that clear and I don't know why you would do any of that but here you have your solution: I have written a function that you can use to limit the cardinality of the subArrays.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function reorderArray(arr) {
let buff = [];
let newArr = [];
let maxSubarrCardinality = 2;
//flatMapping second level elements
//in a single level buffer
for (subArr of arr) {
for (elem of subArr) {
buff.push(elem);
}
}
//Inserting elements one at the time
//into a new array
for (elem in buff) {
//when the new array is empty
//push in the new array the first empty subArray
if (newArr.at(-1) == undefined)
newArr.push([]);
//if the last subArray has reached
//the maxCardinality push a new subArray
else if (newArr.at(-1).length >= maxSubarrCardinality) {
newArr.push([]);
newArr.at(-1).push(elem);
}
//if none of the previous special cases
//just add the element to the last subArray of the newArray
else {
newArr.at(-1).push(elem);
}
}
return newArr;
}
myArray = reorderArray(myArray);

Steps
I used Array#flat, so I had to keep track of the indices and length of each item.
let i = 0;
let itemLength = array[0]?.length;
After flattening the array, I use Array#reduce to loop through the items, setting the initialValue to an empty array.
I get the last item in the array and check if its length has reached the maximum allowed (which should be the one set in the rules argument for that index or the size argument).
If the max hasn't been reached, I push the current item into the last array. If it has, I create a new array, with the item as the only element
array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur]);
If then either decrement the value of itemLength, or set it to the length of the next array. and increment the i variable to the next index
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
let array = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
function conditionalChunk(array, size, rules = {}) {
let i = 0;
let itemLength = array[0]?.length;
return array.flat().reduce((acc, cur) => {
if (acc.length === 0) acc.push([]); // Just for the first iteration
if (acc.at(-1).length < (rules[i] ?? size)) acc[acc.length - 1].push(cur);
else acc.push([cur])
itemLength = itemLength === 0 ? (array[++i] ?? []).length : --itemLength;
return acc;
}, []);
}
console.log(JSON.stringify(conditionalChunk(array, 3, { 0: 2 })));

Here is what I came up with. Paste the code into the chrome console and test it.
var myArray = [[1, 2, 3, 4], [5, 6], [7, 8, 9], [10]];
//Getting initial lengths of inner arrays [4, 2, 3, 1]
var lengths = [];
myArray.forEach((arr) => {lengths.push(arr.length);});
// Extracting the elements of all the inner arrays into one array.
var allElements = [].concat.apply([], myArray);
// Updating the lengths of first and last inner arrays based on your requirement.
var firstArrLen = 2;
var lastArrLen = lengths[lengths.length -1] + (lengths[0] - 2)
lengths[0] = firstArrLen;
lengths[lengths.length -1] = lastArrLen;
// Initializing the final/result array.
var finalArr = [];
// Adding/Pushing the new inner arrays into the finalArr
for(var len of lengths) {
var tempArr = [];
for(var i=0; i<len; i++) {
tempArr.push(allElements[i]);
}
finalArr.push(tempArr);
for(var i=0; i<len; i++) {
// removes the first element from the array.
allElements.shift();
}
}
console.log(finalArr);

Related

removing duplicate elements in an array and also the elements which is repeated most in the array should come first in the new array

const source = [2, 9, 9, 1, 6];
const ans = source.filter((item, index, arr)=> arr.indexOf(item) === index);
console.log(ans);
here i'm able to remove the duplicate elements but how to make 9 which is repeated highest to come first in the new array??
any help would be appreciated
ans should be [9, 2, 1, 6]
This should work for all cases where array should be sorted by most number of reoccurrences.
const source = [2,1,9,9,6];
const indexes = [];
var ans = source.filter((item,index,arr)=>{
if(arr.indexOf(item) === index){
indexes.push({item:item,count:1})
return true;
}
else if(arr.indexOf(item) !== index){
indexes[indexes.findIndex(object=> object.item === item)].count++
return false;
}
return false;
})
ans =(indexes.sort((a,b)=>{return b.count - a.count}).map(obj =>obj.item))
console.log(ans)
If using more space is okay, you can use a hash map for counting elements and then convert it to an array.
Something like this.
let arr = [2, 9, 9, 1, 6];
// count elements
const map = arr.reduce((acc, e) => acc.set(e, (acc.get(e) || 0) + 1), new Map());
// sort by values and convert back to array
const res = [...map.entries()].sort((a, b) => b[0] - a[0]).map((a) => {
return a[0]
});
console.log(res)
Try This
function removeAndSort(arr) {
var order = {};
for (var i = 0; i < arr.length; i++) {
var value = arr[i];
if (value in order) {
order[value]++;
} else {
order[value] = 1;
}
}
var result = [];
for (value in order) {
result.push(value);
}
function sort(a, b) {
return order[b] - order[a];
}
return result.sort(sort);
}
console.log(removeAndSort([2, 9, 9, 1, 6]));
It's Absolutely Working, Check It
Instead of removing the duplicates with the code you have you need to find a way to create a frequency map to save the duplicates information. In this example I create a map using an object like this...
const freq = { 9: [9, 2], 1: [1, 1] ... };
which uses the current iterated element as a key in an object, and the value is the element along with its duplicate value. You can grab those arrays using Object.values, sort them by that duplicate value, and then map over that sorted nested array to produce the result.
Note, however, due to the sorting process this results in [9, 1, 2, 6].
const source = [2, 9, 9, 1, 6];
// `reduce` over the array creating a key/value pair where
// the value is an array - the first element is the element value,
// and the second value is its duplicate value
const nested = source.reduce((acc, c) => {
// If the object property as defined by the element
// value doesn't exist assign an array to it initialising
// the duplicate value to zero
acc[c] ??= [c, 0];
// Increment the duplicate value
++acc[c][1];
// Return the object for the next iteration
return acc;
}, {});
// We take the `Object.values` of the object (a series of
// nested arrays and sort them by their duplicate value,
// finally `mapping` over the sorted arrays and extracting
// the first element of each
const out = Object.values(nested).sort((a, b) => {
return b[1] - a[1];
}).map(arr => arr[0]);
console.log(out);
Additional documentation
Logical nullish assignment
function sortAndFilter(source) {
let duplicates = {};
//count the duplications
source.filter((item, index, arr) => {
if(arr.indexOf(item) != index)
return duplicates[item] = (duplicates[item] || 0) + 1;
duplicates[item] = 0;
})
//sort the numbers based on the amount of duplications
return Object.keys(duplicates).map(a => parseInt(a)).sort((a, b) => duplicates[b] - duplicates[a]);
}
Output: [ 9, 6, 2, 1 ]
This could do the job
this is best answer for your question
const source = [2, 9, 9, 1, 6];
function duplicate(array) {
let duplicates = array.filter((item, index) => array.indexOf(item) !== index);
return duplicates.concat(array.filter((item) => !duplicates.includes(item)));
}
console.log(duplicate(source));
function myFunction() {
const source = [2, 9, 9, 1, 6];
const ans = source.filter((item, index, arr)=> arr.indexOf(item) === index);
ans.sort((a, b) => b-a);
console.log(ans);
}
Output: [ 9, 6, 2, 1 ]

How do I filter all items that occur once into one list and all items that occur multiple times into a different one?

I'm currently working on a project but I'm stuck with removing all the duplicates.
I need to remove all the duplicate names and put into a separate file
This is an example of what Im trying to achieve:
So I have an array of numbers (1,2,2,3,3,4,5) and I would like to remove all of the duplicates from the array to result in (1,4,5).
For loop the array and place each value into a hash map that tracks the number of times that number is recorded. Then loop through your hash map and create a new array with only the values that have a record of 1.
const arr = [1, 2, 2, 3, 3, 4, 5];
function removeDuplicates(arr) {
var hashMap = {};
for(let i of arr) {
if(hashMap[i]){
hashMap[i] += 1
} else {
hashMap[i] = 1
}
}
var newArray = [];
for(let [key, value] of Object.entries(hashMap)){
if(value === 1) {
newArray.push(parseInt(key));
} else {
// If you want to do something with values recorded more
// than once, you can do that here.
}
}
return newArray;
}
Without using any external libraries - I am sure there are more concise ways to do it, but this should work:
var numbers = [1, 2, 2, 3, 3, 4, 5];
function removeDuplicates(array) {
var existingValues = []; // Holds all values that exist at least once
var duplicates = []; // Holds all values that are duplicates
array.forEach(function(num) {
if (existingValues.indexOf(num) === -1) {
existingValues.push(num);
} else {
duplicates.push(num);
}
});
// Filter out the values from existingValues that are in the duplicates array
return existingValues.filter(function(i) {
return duplicates.indexOf(i) === -1;
});
}
console.log(removeDuplicates(numbers)); // [1,4,5]
Will the array always be sorted?
no , but that might be something to consider #Thomas
OK, this would have allowed for something like this:
Just looking at the neighbors to determine wether a value is single or has multiple occurances.
const array = [1,2,2,3,3,4,5];
const single = [];
const multiple = [];
for (let i = 0, length = array.length; i < length; ++i) {
let value = array[i];
const isDupe = i > 0 && value === array[i - 1]
|| i + 1 < length && value === array[i + 1];
if (isDupe) {
multiple.push(value);
} else {
single.push(value);
}
}
console.log("singles", single);
console.log("multiple", multiple);
If the data ain't guaranteed to be sorted we need to do a count pass first the check which items are unique in that array and which ones are not. And in a secnd pass we can add them to the result arrays.
const array = [3, 2, 4, 2, 5, 1, 3];
const single = [];
const multiple = [];
const count = {};
for (let i = 0; i<array.length; ++i) {
let value = array[i];
count[value] = (count[value] || 0) + 1;
}
for (let i = 0; i<array.length; ++i) {
let value = array[i];
if (count[value] > 1) {
multiple.push(value);
} else {
single.push(value);
}
}
console.log("singles", single);
console.log("multiple", multiple);
Based on the input you gave: [1, 2, 2, 3, 3, 4, 5] and the fact you said you wanted two outputs: one with the unique values, [1,4,5], and one with duplicates [2,2,3,3].
The below function will give you two arrays as outputs, one with the unique values, and one with the duplicates.
const getUniqueAndDuplicates = (arr) =>{
//use a JavaScript object as a map to count frequency
const map={};
for(let i=0;i<arr.length;i++){
if(map[arr[i]]){map[arr[i]]++;}
else{map[arr[i]]=1;}
}
const uniqueArray=[];
const duplicateArray=[];
for(let key in map){
//get the frequency count
let freq=map[key];
if(freq===1){uniqueArray.push(key);}
else{
for(let i=0;i<freq;i++){
duplicateArray.push(key);
}
}
}
return [uniqueArray,duplicateArray];
}
There's many ways to remove duplication in array. Here's some samples.
Using Set()
Set objects are collections of values. You can iterate through the
elements of a set in insertion order. A value in the Set may only
occur once
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const duplicated = [1,2,3,2,3,4,3,5];
const uniqSet = new Set(duplicated);
console.log([...uniqSet]) // Should be [1, 2, 3, 4, 5]
Using lodash uniq() method
Document: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set
const _ = require('lodash');
const duplicated = [1,2,3,2,3,4,3,5];
const uniq = _.uniq(duplicated);
console.log(uniq) // Should be [1, 2, 3, 4, 5]
Scratch
const duplicated = [1,2,3,2,3,4,3,5];
const uniq = [];
for (const e of duplicated) {
if (!uniq.includes(e)) {
uniq.push(e)
}
}
console.log(uniq) // Should be [1,2,3,4,5]

Find 2nd largest value in an array that has duplicates of the largest integer

I'm trying to find the second largest number in an array of numbers, but the greatest number appears twice, so I can't just remove it from the array and select the new highest number.
array = [0, 3, 2, 5, 5] (therefore 3 is the 2nd largest value)
I have this code where I can explicitly return 3, but it wouldn't work on other arrays:
function getSecondLargest(nums) {
var sorted_array = nums.sort(function (a,b) {return a - b;});
var unique_sorted_array = sorted_array.filter(function(elem, index, self) {
return index === self.indexOf(elem);
})
return unique_sorted_array[unique_sorted_array.length - 2];
}
return unique_sorted_array[unique_sorted_array.length - 2];
If I wanted to make it more dynamic, is there a way that I could identify the greatest value of the array, then compare that against each iteration of the array?
I was thinking that something along the lines of:
var greatestNum = sortedArray[-1]
while(greatestNum != i) do {
//check for the first number that doesn't equal greatestNum
}
Any help would be appreciated.
You can simply create a Set first and than sort in descending and take the 1st index element
let array = [0, 3, 2, 5, 5]
let op = [...new Set(array)].sort((a,b) => b-a)[1]
console.log(op)
For those who thinks in terms of efficiency. this is the best way IMO
let array = [0, 3, 2, 5, 5]
let max = -Infinity
let secondMax = -Infinity
for(let i=0; i<array.length; i++){
if(array[i] > max){
secondMax = max
max = array[i]
}
}
console.log(secondMax)
I’d recommend doing something more like
const nums = [0, 3, 2, 5, 5];
nums.sort(function (a,b) {return b - a;})
for (let i = 1; i < nums.length; i++) {
if (nums[0] !== nums[i]) {
return nums[i];
}
}
which should be a lot more efficient (especially in terms of memory) than converting to a set and back...
Try this:
var intArray = stringArray.map(nums); // now let's sort and take the second element :
var second = intArray.sort(function(a,b){return b-a})[1];
};
For those who wants to do this using Math.max(). Here's the simplest way to do this.
const getSecondLargest = function (arr) {
const largest = Math.max.apply(null, arr);
for (let i = 0; i < arr.length; i++) {
if (largest === arr[i]) {
arr[i] = -Infinity;
}
}
return Math.max.apply(null, arr);
};
console.log(getSecondLargest([3, 5, 9, 9, 9])); //5
Side note: Math.max() don't take an array, so we have to use Math.max.apply() to pass the array in the function. -Infinity is smaller than any negative finite number.

Finding addenth on array - JS

I created a function that will find pairs to add the two numbers that will be equal to the sum.
function findingSum(arr, sum){
var firstElement = [];
var difference = [];
var final = [];
var convertArr = arr.map(function(item){
return parseInt(item, 10);
});
for(var i = 0; i < convertArr.length; i++){
difference.push(sum - convertArr[i]); // subtracted sum from each convertArr item
if(difference[i] + convertArr[i] === sum){ // check if convertArr item was added to difference item === sum
firstElement.push(convertArr[i]); // if so add the convertArritem to the result array
}
if(firstElement[i] + convertArr[i] == sum){
final.push(firstElement[i], convertArr[i]);
}
}
return final;
}
var addNumbers = findingSum([3, 34, 4, 12, 5, 2], 9);
console.log(addNumbers); // --> [4, 5]
So what I did is that I try to get the difference of convertArr[i] and the sum and put them in a difference variable. Then I tried to see if adding difference[i] from the original array will give me the sum. If so I'll add them on firstElement array and try to add each value to the original array and finally push them along with it's addenth if the sum was attain. So when you add this two you'll get the sum.
For some reason my logic doesn't work and it does'nt push things on both firstElement and final array. Can anyone help me with this?>
You could use a hash table for visited values.
var findingSum = function (array, s) {
var a, i,
hash = Object.create(null);
for (i = 0; i < array.length; i++) {
a = array[i];
if (hash[s - a]) {
return [s - a, a];
}
if (!hash[a]) {
hash[a] = true;
}
}
};
console.log(findingSum([3, 34, 4, 12, 5, 2], 9)); // [4, 5]

How to find the greatest number of times each element occurs in a nested array?

How to find the greatest number of times each element occurs in a nested array?
I am looking to find the greatest number of times each element occurs in any of the subarrays. I am NOT looking for the element that occurs the most times in the entire nested array.
Let's say my nested array is [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]].
The number 2 appears two times in one of the subarrays. The number 3 appears two times in one of the subarrays. The number 5 appears one time in one of the subarrays.
The end result I am looking for is [2, 2, 3, 3, 5].
What is the best way to do this? Below is my approach, which is not very good.
function makeNewArray(arr) {
// add the # to the numbers that appear once and add the ## to the numbers that appear twice
for (var j = 0; j < arr.length; j++) {
for (var i = 0; i < arr[j].length; i++) {
if (arr[j][i] === arr[j][i+1]) {
arr[j][i] = arr[j][i] + '#';
arr[j][i+1] = arr[j][i+1] + '#';
} else {
arr[j][i] = arr[j][i] + '#';
}
}
}
// flatten the array
arr = arr.reduce(function(a, b) { return a.concat(b); });
// remove the duplicates from the array
arr = arr.filter(function(a, b) { return arr.indexOf(a) == b; });
// remove the ## and # from the array
for (var i = 0; i < arr.length; i++) {
arr[i] = parseInt(arr[i]);
}
return arr;
}
makeNewArray([[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]]);
Just based on your question and not the result you expect that I don't really get, here is a working solution that will find the highest number of occurrence.
var a = [
[2, 3, 5],
[3, 3, 5],
[2, 2, 3, 5]
];
var o = {};
var max = 0;
var highest = null;
for (var i = 0; i < a.length; i++) {
for (var j = 0; j < a[i].length; j++) {
if (!o.hasOwnProperty(a[i][j])) {
o[a[i][j]] = 1;
} else {
o[a[i][j]]++;
}
if (o[a[i][j]] > max) {
max = o[a[i][j]];
highest = a[i][j];
}
}
}
//this is the number with the highest occurence
console.log(highest);
This ES6 solution iterates the sub arrays, and creates a map of the values, then in it moves the highest values to a map of the entire array. Afterwards, we map the Map entries (no pan intended) to new arrays that are filled with the numbers according to their highest count, and flatten the result.
var data = [[2, 3, 5], [3, 3, 5], [2, 2, 3, 5]];
var result = [].concat(... // flatten the end result
[... // convert the Map to entries array
data.reduce((r, s) => { // reduce the array into a map of counts
s.reduce((map, num) => map.set(num, (map.get(num) || 0) + 1), new Map) // get a Map of the current sub array counts
.forEach((v, k) => r.set(k, Math.max(r.get(k) || 0, v))); // update the global Map if the sub array count of a number is higher
return r;
}, new Map)]
.map((s) => Array.from({ length: s[1] }, () => s[0]))); // map the entries into new sub arrays
console.log(result);

Categories