Concise way to return a new array of N elements filled with iterated values from another array? Vanilla JavaScript

会有一股神秘感。 提交于 2021-02-11 13:50:28

问题


I have a given array with an undetermined quantity of elements, the array can be numbers or strings, then I need to generate a new array of N elements made from the iterated elements of the first array

I already have a function to do it, but it only works if the original array are consecutive numbers, it doesn't work with strings. I have a gazillion of ideas on how to achieve it. I could just concatenate the array to a new one until its equal or greater than the required quantity of elements, and then set the new array length to the required quantity, but is there a more concise and elegant way to do it?

IDEA 01 codepen

function populateArray(qty) {
  // Array to populate from
  let array = [1,2,3];
  //Determine the Range Length of the array and assign it to a variable
  let min = array[0];
  let max = array[array.length - 1];
	const rangeLength = (max - min + 1);
	//Initialize uniqueArray which will contain the concatenated array
	let uniqueArray = [];
	//Test if quantity is greater than the range length and if it is,
    //concatenate the array to itself until the new array has equal number of elements or greater
	if (qty > rangeLength) {
		//Create an array from the expansion of the range
		let rangeExpanded = Array.from(new Array(rangeLength), (x,i) => i + min);
		while (uniqueArray.length < qty) {
			uniqueArray = uniqueArray.concat(rangeExpanded);
		}
	}
  // Remove additional elements
  uniqueArray.length = qty
	return uniqueArray;
}

console.log(populateArray(13))

IDEA 02 codepen, but it fills the new array 13 times with the whole original array, not iterated items

// FILL A NEW ARRAY WITH N ELEMENTS FROM ANOTHER ARRAY
let array = [1,2,3];
let length = 13;
let result = Array.from( { length }, () => array );
                        
console.log(result);

the expected result is [1,2,3,1,2,3,1,2,3,1,2,3,1] if the original array were made of strings the expected result would be [dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog,cat,sheep,dog]


回答1:


I'll go with @CertainPerformance's answer. But here's a different approach, just for thinking-out-of-the-box purposes

// A function for getting an index up to length's size 
function getIDX(idx, length){
return idx <= length ? idx : getIDX(idx-length, length); 
}


const newArrayLength = 13;
const sourceArray = [1,2,3];
const resultArray = [];
for(let i=0; i< newArrayLength; i++){
resultArray[i]=sourceArray[getIDX(i+1, sourceArray.length)-1];
}

EDIT 1: I was comparing the performance of this approach versus the others here described and it seems that if you wanted to create a very large new array (ex: newArrayLength= 10000) the getIDX() function takes a lot to finish because of the size of the call stack. So I've improved the getIDX() function by removing the recursion and now the complexity is O(1) check it out:

function getIDX(idx, length){
if (length === 1) {return idx};
const magicNumber = length * (Math.ceil(idx/length)-1);
 return idx - magicNumber;
}

With the new getIDX() function this approach seems to be the most performant. You can take a look to the tests here: https://jsbench.me/v7k4sjrsuw/1




回答2:


You can tweak your second idea a bit - calculate the number of times you need to repeat the initial array to come up with the required number of total items, then flatten it and .slice:

let array = [1,2,3];
let length = 13;
const fromLength = Math.ceil(length / array.length);
let result = Array.from( { length: fromLength }, () => array )
  .flat()
  .slice(0, length);

console.log(result);



回答3:


You can use a generator function that will create a repeating sequence from an input. You can add a limit to the generated sequence and simply turn it into an array:

function* repeatingSequence(arr, limit) {
  for(let i = 0; i < limit; i++) {
    const index = i % arr.length;
    yield arr[index];
  }
}

const generator = repeatingSequence(["dog", "cat", "sheep"], 10);

const result = Array.from(generator);

console.log(result);

Alternatively, you can make an infinite repeating sequence with no limit and then generate as many elements as you want for an array:

function* repeatingSequence(arr) {
  let i = 0
  while(true) {
    const index = i % arr.length;
    yield arr[index];
    i++;
  }
}

const generator = repeatingSequence(["dog", "cat", "sheep"]);

const result = Array.from({length: 10}, () => generator.next().value);

console.log(result);



回答4:


You can use modulo operator. Special thanks to @Vlaz for shorten version:

Array.from({ length:length }, (e, i) => array[ i  % array.length ])

An example:

let array = [1,2,3];
let length = 13;
const result = Array.from({ length:length }, 
    (e, i) => array[ i  % array.length ]);
console.log(result);


来源:https://stackoverflow.com/questions/59529059/concise-way-to-return-a-new-array-of-n-elements-filled-with-iterated-values-from

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!