0

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 just reminded why the first function needs rangeLength and rangeExpanded, the server just gives me starting value and ending value, not an array. Commented Dec 30, 2019 at 11:12

4 Answers 4

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);

Sign up to request clarification or add additional context in comments.

3 Comments

good and thank you very much it works as expected. Anyway in the example 2 i asked if it could be iterated and probably the function needs a map so I can transform each iterated item.
With this method, since you're flattening, there doesn't look to be any need for transforming otherwise, right?
Probably I would remove fromLength and slice it anyway to keep it concise, need to test performance with big arrays. On the other hand Imagine on each iteration I prepend or append something to each array element, imagine if the original array were [dog, sheep, cow] then the new array would be [black-dog, black-sheep, black-cow, pink-dog, pink-sheep, pink-cow, green-dog...] or even random generated values prepended to each iterated item.
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

3 Comments

Good job, nice recursion. As you said, to think out of the box.
WOW, even the generator is up to 40% slower. I did run independent benchmarks from the ones you sent. I still won't go with @CertainPerformance answer, it can be one liner but flat is only supported from chrome 69. Still doing tests for now. Yours look more elegant. But I think in your solution performance is tricked. running the for loop without a function looks like a bad practice. I converted the loop to a pure function and it is 50.48% slower codepen.io/aalvarados/pen/bGNopJg
I'll investigate further and edit the answer accordingly.
1

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);

Comments

0

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);

2 Comments

array[i] ? array[i] : array[ i % array.length ] what is the benefit of the conditional operator over a simple array[ i % array.length ]? For i < array.length : i === i % array.length, so the first branch seems rather useless.
@VLAZ you are right! I was in hurry! Thank you very much for the great comment! I've updated the answer!:)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.