How to insert a duplicate item at an index of an array? - javascript

I use Angular 6 . I need to create a duplicate element in FormArray. I need to create method createACopyAtIndex(indexNumber: number) so that when one passes indexNumber == 3, then a duplicate element is created and assigned index of 4. The new array will have two duplicate elements: 3 and 4. I tried this
createACopyAtIndex(indexNumber: number) {
console.log(indexNumber);
var myArray = <FormArray>this.invoiceForm.controls['test'].value;
this.invoiceForm.controls['test'].patchValue(this.getDuplicatedArrayAtIndex(myArray, indexNumber));
}
// this method returns a new array newArr
// this new array contains two duplicate elements: indexNumber and indexNumber + 1
getDuplicatedArrayAtIndex(arr, indexNumber) {
var temp = arr[indexNumber];
var newArr = arr.splice(indexNumber, 0, temp);
return newArr;
};
But nothing happens. No error. No changes

splice neither returns a new array nor the old mutated array. It reutns deleted items from arr. In your case, an empty array
If you want to return a new array with a duplicate at the index specified, you can can take a copy of the original array using slice. Then use splice to insert at the index specified. Then, return the new array
function getDuplicatedArrayAtIndex(arr, indexNumber) {
var newArr = arr.slice();
newArr.splice(indexNumber, 0, arr[indexNumber])
return newArr
};
console.log(getDuplicatedArrayAtIndex([0,1,2,3,4], 2))
Another option is to slice the 2 parts of the arrays and insert a new item at the index and merge them using spread syntax
const getDuplicatedArrayAtIndex =
(arr, i) => [...arr.slice(0, i + 1), arr[i], ...arr.slice(i + 1)]

Related

Puh array in empty array that create before

I push two Array in one Array But can't use them as a string,This result in bellow about two arrays in one array :
Array(0) [] //This is empty array that I create before and push these two array bellow to them
length:2
0:Array(1) ["cfdb9868-0f69-5781-b1e4-793301280788"]
1:Array(1) ["cfdb9868-0f69-5781-b1e4-793301280788"]
and I create a for for access them but I can ! I write this code "
for(var index = 0 ; index < Array.length ; ++index) {
let Each_String_In_Brackets = Array[index] ;
console.log(Each_String_In_Bruckets);
}
Why is this happen!
I mean Why when we push array in empty array can't access them!
I want to access the content of them, I have a string In each bracket.
var arr = [];
arr.push(["cfdb9868-0f69-5781-b1e4-793301280788"]);
arr.push(["cfdb9868-0f69-5781-b1e4-793301280788"]);
//assuming inside array always will be one element:
arr.forEach((item)=>{ console.log(item[0])})
//if inside array may be multiple elements, then use this
arr.forEach((item, index)=>{
item.forEach((child)=>{ console.log(child)})
})
pushing a full array into another array makes it a 2D array ( at the indexes where you push another array ) so for example if I have the first array
BArray[]
But if I push another array into it
BArray2 = [1,2,3,4];
BArray.push(Array2);
We would not be able to access it just by
BArray[0]
This would return the entire array2 rather the content of array2 at index 0.
Therefore you would do this
BArray[0][0]
So this would give us ( from your array ) "cfdb9868-0f69-5781-b1e4-793301280788"
If you would like to just dump out the content of BArray2 into BArray
You can use the spread operator.
BArray[...BArray2];
( Also I would not use Array as variable name ! It can be confusing as new Array(10); is a way of creating arrays and having arrays with that name isn't best practice ! )
Hope this helps !
You are pushing array in array, so you must access as 2D array:
var array = [];
var arrayString1 = ["StringInArray1"],
arrayString2 = ["StringInArray2"];
array.push(arrayString1);
array.push(arrayString2);
console.log(JSON.stringify(array));
array.forEach(arrayItem => {
console.log("StringInArray: " + arrayItem[0]);
})
Or maybe you want to append array:
var array = [];
var arrayString1 = ["StringInArray1"],
arrayString2 = ["StringInArray2"];
[].push.apply(array, arrayString1);
[].push.apply(array, arrayString2);
console.log(JSON.stringify(array));
array.forEach(arrayItem => {
console.log("StringInArray: " + arrayItem);
})

Possible to push empty slot to an array?

I'm building my own map method to be as close as the native map method.
Since the native map pushes(i think) the changed values into a new array, it still keeps the empty slots. I wasn't able to find a solution to push an empty slot into an array, like this example below.
[1, 2, 3].push(some code) // [1, 2, 3, empty]
I tried pushing an array with one empty item prefixed with a spread operator arr.push(...(new Array(1))) or arr.push(...[,]) but that just pushes undefined.
I solved my problem by not using push and instead assigning values to the array index that way skipped indices will be set to empty.
But I'm writing this post to see if anyone knows that if it's possible to use the push method to push an empty slot to an array.
No, it's not possible, not with the push method. empty can only exist if the array has a certain length, but a whole number property of the array does not exist at some index. This is called a sparse array, and cannot be created with push (or other array methods, if they're called on and with non-sparse arrays).
The only way to do so would be to assign to an index for which a lower index doesn't exist yet.
Look at the results for the below two snippets in your browser console, not the snippet console:
const arr = [];
arr[1] = 'a';
console.log(arr);
Or to set the .length of the array above the last index that the array has:
const arr = [];
arr.length = 1;
console.log(arr);
But the two approaches above are very weird to do and probably have no good reason to be used. Better to avoid sparse arrays entirely.
Keep in mind that an empty slot is different from undefined, which is perfectly possible to have as an array value:
const arr = [];
arr.push(undefined);
console.log(arr);
You can create an empty slot in an array by incrementing the array length:
var a = []
a.push(1)
a.length++
a.push(3)
console.log(a)
console.log(1 in a) // anything at index 1?
Alternatively, you can push something and then delete it:
var a = []
a.push(1)
a.push(2)
a.push(3)
delete a[1]
console.log(a)
console.log(1 in a) // anything at index 1?
There is no need to actually push to a new array in your implementation. You can simply do new Array(this.length) where this.length is the array you are mapping through length.
For example consider this map implementation:
if (!Array.prototype.mapIt) {
Object.defineProperty(Array.prototype, "mapIt", {
value: function(fn) {
if (this === null) {
throw new TypeError('Array.prototype.mapIt called on null or undefined');
}
if (typeof fn !== 'function') {
throw new TypeError('predicate must be a function');
}
let _array = this.filter(x => x != null) // remove empty values
let result = new Array(_array.length) // the new array we will return
for (var i = 0; i < _array.length; i++) {
result[i] = fn.call(arguments[1], _array[i], i, _array) // call the predicate
}
return result;
}
});
}
let arr = [1, 2, , , 3] // the test array
let result = arr.mapIt((c, i, a) =>
console.log(`current: ${c}`, `index: ${i}`, `array: ${a}`) || c + 2)
console.log('result: ', result)
console.log('original array: ', arr)
Hope this helps you with an gives you an idea about a possible map implementation.

How to duplicate elements in a js array without creating "dependent elements"?

I am trying to modify a single element from an array whose elements were previously duplicated n times. To perform the array duplication I just relied on a custom function duplicateElements(array, times)from this post (see #Bamieh answer). As shown in the exemple below, the problem is I can't modify a single element from the array without modifying other elements:
function duplicateElements(array, times) {
return array.reduce((res, current) => {
return res.concat(Array(times).fill(current));
}, []);
}
var myvar = duplicateElements([{ a: 1 }, { a: 2 }], 2);
myvar[0].a = 3;
console.log(myvar);
// (4) [{…}, {…}, {…}, {…}]
// 0: {a: 3}
// 1: {a: 3}
// 2: {a: 2}
// 3: {a: 2}
// length: 4
As you can see myvar[1].a was also modified although this wasn't intended. How can I avoid this issue?
The problem is that you're passing the reference to the original object in Array(times).fill(current) .
In this case the two copies of the first {a:2} are the same copy of the original (They reference to the same space in memory) so if you change one, the two of them will change as they reference the same object in memory.
You have to make a deepcloning function or maybe spread the object inside a new one. You can change your original function to work with objects and primitives like this:
function duplicateElements(elementsArray, times) {
//Make a new placeholder array
var newArray = [];
//Loop the array of elements you want to duplicate
for (let index = 0; index < elementsArray.length; index++) {
//Current element of the array of element
var currentElement = elementsArray[index];
//Current type of the element to check if it is an object or not
var currentType = typeof currentElement
//Loop over the times you want to copy the element
for (let index = 0; index < times; index++) {
//If the new element is not an object
if (currentType !== "object" && currentType){
//append the element
newArray.push(currentElement)
//if it is an Object
} else if (currentType === "object" && currentType){
//append an spreaded new Object https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax
newArray.push({...currentElement})
}
}
}
return newArray;
}
This is not the optimal way to do this, but I think that maybe you're new to javascript and is better to learn the old way of looping before using more Array functionalities (as the answer from Jonas Wilms, that is also a good answer).
I would recommend javascript.info and eloquent javascript to learn more about the language
The main reason for this as specified in the Array.fill documentation is that when dealing with objects it will copy by reference:
When fill gets passed an object, it will copy the reference and fill
the array with references to that object.
With lodash (and _.cloneDeep) that is one line like this:
let dubFn = (arr, t=1) =>
_.concat(arr, _.flatMap(_.times(t, 0), x => _.cloneDeep(arr)))
let r1 = dubFn([{a:1},{b:3}]) // no parameter would mean just 1 dub
let r2 = dubFn([{a:1},{b:3},5,[1]], 2) // 2 dublicates
r1[0].a = 3
r2[0].a = 3
console.log(r1)
console.log(r2)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
Note that this now works with arrays/objects and primitives.
The idea is to use _.concat to return a new concatenated version of the input array with a combination of few functions which on the end return an array of cloned objects. We use _.times to return an array of in this case t elements and then for each of those elements we replace with a deep clone of the array. _.flatMap is needed to flatten the end result since we end up having array of arrays after the _.times call.
With ES6 you can do something like this:
let dubElements = (arr, t) =>
[...arr, ...new Array(t).fill().flatMap(x => arr.map(y => ({...y})))]
let r1 = dubElements([{a:1},{b:3}])
let r2 = dubElements([{a:1},{b:3}],2)
r1[0].a = 3
r2[0].a = 3
console.log(r1)
console.log(r2)
Where we concat arrays via the spread operator and we use new Array(t) to create the new duplicates array and make sure we fill it with undefined in this case after which we flatMap the results (which we map through the clone via the spread operator again.
Note that this works for your use case specifically. If you want to make it more generic you have to expand more in the last map function etc.
If you want to preserve the order of the elements you can do something like this:
let dubElements = (arr, t=1) => {
let _result = []
arr.forEach(x => {
for(let i=0; i<t+1; i++) {
_result.push({...x})
}
})
return _result
}
let result = dubElements([{a:1},{b:3}],2)
result[0].a = 3
console.log(result)
Replace
Array(times).fill(current)
which will add one reference to current multiple times to the array with:
Array.from({ length: times }, () => ({...current }))
which will shallow clone current. Note that the code will then only work with objects though, not with primitives.
I'd do:
const duplicateElements = (array, length) =>
array.flatMap(current => Array.from({ length }, () => ({ ...current }));

What's the best way to split an array into multiple arrays every time an element is met?

I'm fairly new to JS and still learning. I have this array:
[A,true,B,true,C,true,D,true,E,A,true,B,true,C,false,E,A,true,B,false,E]
What's the best way to split said array into multiple arrays everytime E occurs?
ie. the above array becomes:
[A,true,B,true,C,true,D,true,E]
[A,true,B,true,C,false,E]
[A,true,B,false,E]
What I have tried:
I have tried converting the array into a string and splitting it, yet the result isn't satisfactory.
var pathsString = paths.toString();
pathsString = pathsString.split("," + end).filter(function(el) {return el.length != 0});
//end is E in this case
Thank you
You can use Array.reduce() and Array.filter():
const arr = ['A',true,'B',true,'C',true,'D',true,'E','A',true,'B',true,'C',false,'E','A',true,'B',false,'E'];
const result = arr.reduce((acc, x) => {
acc[acc.length-1].push(x)
if (x === 'E') acc.push([]);
return acc;
}, [[]]).filter(x => x.length);
console.log(result);
Here are some explanations:
Array.reduce() iterates over the an array and passes an accumulator from one iteration to the next. This accumulator (acc) is initialized to [[]] at the beginning (an array with an empty group).
At each iteration, we take the last group in acc and append the value x to that group.
If the value equals E, we also push a new empty group for the next iteration.
Then, we filter out the empty groups left out if E was the last letter or if the input array was empty.
You can chunk the array with Array.reduce():
const arr = ['A',true,'B',true,'C',true,'D',true,'E','A',true,'B',true,'C',false,'E','A',true,'B',false,'E']
const result = arr.reduce((r, c, i) => {
if(!i || c === 'E') r.push([]) // if it's the 1st item, or the item is E push a new sub array
r[r.length - 1].push(c) // push the item to the last sub array
return r
}, [])
console.log(result)
var original = ['A',true,'B',true,'C',true,'D',true,'E','A',true,'B',true,'C',false,'E','A',true,'B',false,'E'];
// make a copy of the original, just so we are not changing it
var temp = original.slice(0);
// the variable we will collect all the sub arrays in
var result = [];
// while the temp still has data to evaluate, loop
while (temp.length) {
// find the next position of the first character in the temp array
// add one since the index was affected by us slicing off the first character
var lastIndex = temp.slice(1).indexOf(temp[0]) + 1;
// if the lastIndex is not 0, add the sub array
if (lastIndex) {
// push the sub array
result.push(temp.slice(0, lastIndex));
// remove the sub array from temp so its not processed again
temp = temp.slice(lastIndex);
} else {
// index was zero, so the indexOf was -1 (not found), so it's the last subarray
result.push(temp);
// set to empty list so the loop ends
temp = [];
}
}
console.log(result);

Javascript - Get Array of indices for 'WILL BE' relationship after sort

I have a very simple question. Say I have an array
a = [10,40,30,20,60,50]
After sorting, it would be (assuming I use sort_a = a.sort())
sort_a = [60,50,40,30,20,10]
I want to create an array of indices from a which specify which location in the sorted array that element WILL BE after sorting. From the above example, the result would be
a_sortedindices = [6, 3, 4, 5, 1, 2]
..meaning 10 is in the 6th position when sorted, 40 is in the 3rd position... etc
Pair the values with their current indices
Sort the array of pairs based on the original values
Combine the pairs with their new indices
Sort the new array based on the original indices
Obtain the new indices from the sorted array
let values = [10,40,30,20,60,50];
let indices = values
.map((v, i) => ({ v, i }))
.sort((l, r) => r.v - l.v)
.map(({v, i}, i2) => ({ v, i, i2 }))
.sort((l, r) => l.i - r.i)
.map(p => p.i2);
console.log(indices);
This results in an array of 0-based indices because JavaScript uses 0-based indices. If you want 1-based indices like in your question, you can change p.i2 to p.i2 + 1 in the second to last line.
One of the ways, apart from many to achieve this:
1) Transform the array into another with old indices
2) Sort the array in descending order
3) Create an answer array since you now know the old and new indices.
let a = [10,40,30,20,60,50];
let transformed = a.map((v,i)=> {
return {num:v,old:i};
});
transformed.sort((a,b)=> {
return b.num - a.num;
});
let ans = [];
transformed.forEach((v,i) => {
ans[v.old] = i+1;
});
console.log(ans);
Not sure if this is a trick question or if you're trying to find the most minimal method for achieving this, but you basically already have it. This is what I came up with:
var a = [10,40,30,20,60,50];
var sort_a = a.slice(0).sort((a1,a2) => a2 - a1);
var a_sortedindices = a.map( a1 => sort_a.indexOf(a1) + 1 );
console.log(a_sortedindices);
Walking through it, I'll explain each part.
First, off you have to sort it. Looks like you need reverse sorting, so we'll add an arrow function describing a reverse sort, but before we do that, we'll also clone the array, otherwise we'll lose the original indexes of the values. .slice(0) is a nice way to return a clone of an array
var sort_a = a.slice(0).sort((a1,a2) => a2 - a1);
Then we'll map each value of the origin array. .map() is nice and easy to quickly manipulate each element in an array. We use .indexOf() to figure out where it was at in the original array. We add one to that value because you're not using zero-based indexing.
var a_sortedindices = a.map( a1 => sort_a.indexOf(a1) + 1 );
And voila. You have the sorted indexes.
A naive way of doing this job could be;
var arr = [10,40,30,20,60,50],
idx = arr.map(function(n){return this.indexOf(n)+1;}, arr.slice().sort((a,b) => b-a));
console.log(idx);
where the this argument for the .map() function callback is arr.slice().sort((a,b) => b-a)
// the array to be sorted
var list = [10,20,30,40];
// temporary array holds objects with position and sort-value
var mapped = list.map(function(el, i) {
return { index: i, value: el };
})
// sorting the mapped array
mapped.sort(function(a, b) {
return b.value - a.value;
});
// you can then remap the sorted mapped array to hold just the indices
P.S.: For future reference MDN is your friend

Categories

Resources