How to remove an array element without splice and without undefined remaining? - javascript

Is it possible to remove an array element at a certain position, without rearranging indexes, and without that position changing to undefined?
I don't think that is possible with delete nor splice?
I need an accurate way to view the length of the array, without rearranging indexes.
I do not want to use splice because i have an object that has specific positions mapped to actual X,Y points of a tabel (Punkt).
UPDATE: actually, knowing if the array element exists out of ONLY undefined values might also help me, is there an easier way then looping through?
var keys = Object.keys(racks);
for (var i = 0; i < keys.length; i++)
{
for (var x = 0; x < racks[keys[i]].punkt.length; x++)
{
if(racks[keys[i]].punkt[x].y == fullName)
{
//delete racks[keys[i]].punkt[x];
racks[keys[i]].punkt.splice(x,1);
console.log(keys[i] + " : " + racks[keys[i]].punkt.length);
}
}
}

I don't think that is possible with delete nor splice?
I need an accurate way to view the length of the array, without rearranging indexes.
Then delete, a hasOwnProperty or in guard when retrieving from the array, and a loop counting the elements (or a separate variable keeping track) is the only way to do it. JavaScript's standard arrays are inherently sparse (because they're not really arrays at all), they can have gaps in them where they don't have entries. To create a gap, delete the array entry using delete.
Example:
// Setup
var a = ["a", "b", "c", "d"];
console.log(a.length); // 4
// Using delete
delete a[2]; // Delete the entry containing "c"
console.log(a.length); // Still 4
a.hasOwnProperty(2); // false
// Using the guard when getting an entry
if (a.hasOwnProperty(2)) { // Or `if (2 in a)`
// Get and use [2]
}
else {
// Do whatever it is you want to do when the array doesn't have the entry
}
// Finding out how many it *really* has:
var key;
var count = 0;
for (key in a) {
if (a.hasOwnProperty(key) && // See below
/^0$|^[1-9]\d*$/.test(key) &&
key <= 4294967294
) {
++count;
}
}
console.log(count); // 3
See this other answer for the details behind that if in the loop. If you never put non-element properties on the array, you can skip the second and third parts of that if.

This works perfectly.
var delrow = window.event.srcElement;
while ((delrow = delrow.parentElement) && delrow.tagName != "TR");
delrow.parentElement.removeChild(delrow);

var punten = racks[keys[i]].punkt.length;
if(racks[keys[i]].punkt[x].y == fullName)
{
delete racks[keys[i]].punkt[x];
punten--;
}
if(punten==0)
{
console.log("Could have removed device: " + keys[i]);
}

Related

Custom array function for MIN() in Google Sheets

Since the MIN() function in Sheets only returns a single value and there is no way to make it work with ARRAYFORMULA, I wanted to make a custom function that would take two arrays and compare the values at each entry, and return an array of the minimums. (I know there's a workaround that uses QUERY, but it wasn't going to work for my purposes)
What I have right now will take two arrays with one row and work perfectly. Unfortunately, it breaks when more than one row is introduced. I'm not sure why, so I'm lost on how to move forward. How can I make it work for any size arrays?
When I feed it any two dimensional range, it throws an error:
"TypeError: Cannot set property '0' of undefined"
on this line finalarray[x][y] = Math.min(arr1[x][y], arr2[x][y]);
The current ""working"" code:
function MINARRAY(arr1, arr2) {
if (arr1.length == arr2.length && arr1[0].length == arr2[0].length)
{
var finalarray = [[]];
for (x = 0; x < arr1.length; x++)
{
for(y = 0; y < arr1[x].length; y++)
{
finalarray[x][y] = Math.min(arr1[x][y], arr2[x][y]);
}
}
return finalarray;
}
else
{
throw new Error("These arrays are different sizes");
}
}
finalarray is a 2D array. [[]] sets only the first element of finalarray to a array. It is needed to set all elements of finalarray to a array. Inside the loop, add
finalarray[x]=[]//set `x`th element as a array or finalarray[x]= finalarray[x] || []
finalarray[x][y] = Math.min(arr1[x][y], arr2[x][y]);
Alternatively,
finalarray[x] = [Math.min(arr1[x][y], arr2[x][y])]

How to detect duplicate characters in an Array, and create an argument accordingly?

Good evening, I attempting to detect duplicate characters in a string. More specifically, I am trying to find up to two different duplicates within an Array. If there is one duplicate, add a sub-string, and if there is another duplicate, add a different sub-string. Is there any way to do this?
Here is some example code I have so far:
var CodeFieldArray = ["Z80.0", "Z80.1", "Z80.0", "Z70.4"];
/* We have an array here used to create the final string at the end of the
code. It is a dummy array with similar variables in my actual code. For
reference sake, there may be only one object in the array, or 7 total,
depending on the user's input, which is where the duplicate detection should
come in, in case the user enters in multiples of the same code. */
var i, Index;
for (i = 0, L = 0; i < CodeFieldArray.length; i++) {
Index = CodeFieldArray[i].indexOf(CodeFieldArray[i]);
if(Index > -1) L += 1;
Extra0 = CodeFieldArray.indexOf("Z80.8");
Extra1 = CodeFieldArray.indexOf("Z80.9");
if(L >= 2 && Extra0 == -1) CodeFieldArray.push("Z80.8");
Extra0 = CodeFieldArray.indexOf("Z80.8");
if(L >= 4 && Extra0 != -1 && Extra1 == -1) CodeFieldArray.push("Z80.9");
console.println(Extra0);
}
/*^ we attempted to create arguments where if there are duplicates
'detected', it will push, "Z80.8" or, "Z80.9" to the end of the Array. They
get added, but only when there are enough objects in the Array... it is not
actually detecting for duplicates within the Array itself^*/
function UniqueCode(value, index, self) {
return self.indexOf(value) === index;
}
CodeFieldArray = CodeFieldArray.filter(UniqueCode);
FamilyCodes.value = CodeFieldArray.join(", ");
/* this is where we turn the Array into a string, separated by commas. The expected output would be "Z80.0, Z80.1, Z70.4, Z80.8"*/
I have it to where it will add "Z80.8" or "z80.9" if they are not present, but they are being added, only if there are enough objects in the Array. My for-loop isn't detecting specifically the duplicates themselves. If there was a way to detect specifically the duplicates, and create an argument based off of that, then we would be doing grand. The expected output would be "Z80.0, Z80.1, Z70.4, Z80.8"
You can use Set and forEach and includes
var CodeFieldArray = ["Z80.0", "Z80.1", "Z80.0", "Z70.4"];
let unique = [...new Set(CodeFieldArray)];
let match = ['Z80.8','Z80.9'];
let numOfDup = CodeFieldArray.length - unique.length;
if(numOfDup){
match.forEach(e=>{
if(!unique.includes(e) && numOfDup){
unique.push(e);
numOfDup--;
}
})
}
console.log(unique.join(','))
So the idea is
Use Set to get unique values.
Now see the difference between length of original array and Set to get number of duplicates.
Now will loop through match array and each time we push item from match array into unique we reduce numOfDup by so ( to handle case where we have only one duplicate or no duplicate ).
In the end join by ,
You could do something like this:
var uniqueArray = function(arrArg) {
return arrArg.filter(function(elem, pos,arr) {
return arr.indexOf(elem) == pos;
});
};
uniqueArray ( CodeFieldArray )

Create a immutable copy of a property

I have a situation where I want to make a unchangeable copy of a property to restore state to its original... well state.
I have a array of group objects.
Inside each group i have and array of items.
When I make the copy bellow everything is fine.
I start by doing this.
componentDidMount(){
// originalGroups = Object.assign([], this.props.modalitygroups);
originalGroups = JSON.parse(JSON.stringify(this.props.modalitygroups));
},
I have tried both of the statements above, but have read that the current active one makes a true deep copy of a object. Needles to say it does copy it properly.
I then have THIS search feature to search for items in the groups and items.
_searchFilter:function(search_term){
this.setState({modalitygroups:originalGroups});
let tempGroups = Object.assign([], this.state.modalitygroups);
if(search_term !== ''){
for (let x = (tempGroups.length) - 1; x >= 0; x--)
{
console.log("originalGroups",x,originalGroups);
for (let i = (tempGroups[x].items.length) - 1; i >= 0; i--)
{
if(!tempGroups[x].items[i].description.toLowerCase().includes(search_term.toLowerCase())){
tempGroups[x].items.splice(i,1);
}
}
if(tempGroups[x].items.length === 0){
tempGroups.splice(x, 1);
}
}
this.setState({modalitygroups:tempGroups});
}
},
So I start of by restoring the original state to enable searching through everything. The search feature loops though each groups and within each group loop I loop through each item deleting items that dont contain the search phrase.
After looping through each item, if no item remain in the group, I remove that group from the group array aswell.
This works well first time arround.
But when I start searching for a new item, I find that the originalGroups has changed aswell. The previous deleted items has been removed from the unchangable copy aswell and I dont know why. Where and why does it change my safe copy?
Hope this makes sense.
So modality groups contains original groups? This is hard to follow... Instead of 'saving' the original groups, I'd leave this.props.modalitygroups alone and copy to a filteredGroups of the state. You can reload that from the props that you never change.
In your filter function when you do let tempGroups = Object.assign([], this.state.modalitygroups); that should probably be where you use json to create a deep copy. That is filling the new array with the same group references in the old array, so you are modifying the same group instance in the original.
_searchFilter:function(search_term){
// deep copy
let tempGroups = JSON.parse(JSON.stringify(this.props.modalitygroups));
if(search_term !== ''){
for (let x = (tempGroups.length) - 1; x >= 0; x--)
{
console.log("originalGroups",x,originalGroups);
for (let i = (tempGroups[x].items.length) - 1; i >= 0; i--)
{
if(!tempGroups[x].items[i].description.toLowerCase().includes(search_term.toLowerCase())){
tempGroups[x].items.splice(i,1);
}
}
if(tempGroups[x].items.length === 0){
tempGroups.splice(x, 1);
}
}
this.setState({modalitygroups:tempGroups});
}
},
const state = {
property1: 42
};
const originalGroups = Object.freeze(state);
originalGroups.property1 = 33;
// Throws an error in strict mode
console.log(originalGroups.property1);
// expected output: 42
Essentially ReactJS is still javascript afterall, so you can apply Object.freeze to save a copy of state

Javascript checking whether string is in either of two arrays

I'm pulling my hair out over this one. I have two arrays, likes & dislikes, both filled with about 50 strings each.
I also have a JSON object, data.results, which contains about 50 objects, each with an _id parameter.
I'm trying to check find all the objects within data.results that aren't in both likes and dislikes.
Here's my code at present:
var newResults = []
for(var i = 0; i<data.results.length; i++){
for(var x = 0; x<likes.length; x++){
if(!(data.results[i]._id == likes[x])){
for(var y = 0; y<dislikes.length; y++){
if(!(data.results[i]._id == dislikes[y])){
newResults.push(data.results[i]);
console.log("pushed " + data.results[i]._id);
}
else
{
console.log("They already HATE " + data.results[i]._id + " foo!"); //temp
}
}
}
else
{
console.log(data.results[i]._id + " is already liked!"); //temp
}
}
}
As you can see, I'm iterating through all the data.results objects. Then I check whether their _id is in likes. If it isn't, I check whether it's in dislikes. Then if it still isn't, I push it to newResults.
As you might expect by looking at it, this code currently pushes the result into my array once for each iteration, so i end up with a massive array of like 600 objects.
What's the good, simple way to achieve this?
for (var i = 0; i < data.results.length; i++) {
isInLiked = (likes.indexOf(data.results[i]) > -1);
isInHated = (dislikes.indexOf(data.results[i]) > -1);
if (!isInLiked && !isInHated) {
etc...
}
}
When checking whether an Array contains an element, Array.prototype.indexOf (which is ECMAScript 5, but shimmable for older browsers), comes in handy.
Even more when combined with the bitwise NOT operator ~ and a cast to a Boolean !
Lets take a look how this could work.
Array.prototype.indexOf returns -1 if an Element is not found.
Applying a ~ to -1 gives us 0, applying an ! to a 0 gives us true.
So !~[...].indexOf (var) gives us a Boolean represantation, of whether an Element is NOT in an Array. The other way round !!~[...].indexOf (var) would yield true if an Element was found.
Let's wrap this logic in a contains function, to simply reuse it.
function contains (array,element) {
return !!~array.indexOf (element);
}
Now we only need an logical AND && to combine the output, of your 2 arrays, passed to the contains function.
var likes = ["a","b","f"] //your likes
var dislikes = ["c","g","h"] //your dislikes
var result = ["a","c","d","e","f"]; //the result containing the strings
var newresult = []; //the new result you want the strings which are NOT in likes or dislikes, being pushed to
for (var i = 0,j;j=result[i++];) //iterate over the results array
if (!contains(likes,j) && !contains (dislikes,j)) //check if it is NOT in likes AND NOT in dislikes
newresult.push (j) //if so, push it to the newresult array.
console.log (newresult) // ["d","e"]
Here is a Fiddle
Edit notes:
1. Added an contains function, as #Scott suggested
Use likes.indexOf(data.results[i]._id) and dislikes.indexOf(data.results[i]._id).
if (likes.indexOf(data.results[i]._id) != -1)
{
// they like it :D
}
Try first creating an array of common strings between likes and dislikes
var commonStrAry=[];
for(var i = 0; i<likes.length; i++){
for(var j=0; j<dislikes.length; j++){
if(likes[i] === dislikes[j]){
commonStrAry.push(likes[i] );
}
}
}
then you can use this to check against data.results and just remove the elements that don't match.

Javascript: Generic get next item in array

I am trying to make a JavaScript function that will search an array of strings for a value and return the next string. For example, if an array is built such that an item is followed by its stock code, I want to search for the item and have the stock code written.
var item = (from user input); //some code to get the initial item from user
function findcode(code){
var arr = ["ball", "1f7g", "spoon", "2c8d", "pen", "9c3c"]; //making the array
for (var i=0; i<arr.lenth; i++){ //for loop to look through array
arr.indexOf(item); //search array for whatever the user input was
var code = arr(i+1); //make the variable 'code' whatever comes next
break;
}
}
document.write(code); //write the code, I.e., whatever comes after the item
(I'm sure it's obvious I'm new to JavaScript, and while this is similar to a number of other questions I found, those seemed to have more involved arrays or more complex searches. I can't seem to simplify them for my needs.)
You've almost got it right, but the syntax is arr[x], not arr(x):
index = array.indexOf(value);
if(index >= 0 && index < array.length - 1)
nextItem = array[index + 1]
BTW, using an object instead of an array might be a better option:
data = {"ball":"1f7g", "spoon":"2c8d", "pen":"9c3c"}
and then simply
code = data[name]
Cycled items from array this might be useful
const currentIndex = items.indexOf(currentItem);
const nextIndex = (currentIndex + 1) % items.length;
items[nextIndex];
The first item will be taken from the beginning of the array after the last item
Try this String.prototype function:
String.prototype.cycle = function(arr) {
const i = arr.indexOf(this.toString())
if (i === -1) return undefined
return arr[(i + 1) % arr.length];
};
Here is how you use it:
"a".cycle(["a", "b", "c"]); // "b"
"b".cycle(["a", "b", "c"]); // "c"
"c".cycle(["a", "b", "c"]); // "a"
"item1".cycle(["item1", "item2", "item3"]) // "item2"
If you want to do it the other way round, you can use this Array.prototype function:
Array.prototype.cycle = function(str) {
const i = this.indexOf(str);
if (i === -1) return undefined;
return this[(i + 1) % this.length];
};
Here is how you use it:
["a", "b", "c"].cycle("a"); // "b"
["a", "b", "c"].cycle("b"); // "c"
["a", "b", "c"].cycle("c"); // "a"
["item1", "item2", "item3"].cycle("item1") // "item2"
I think that an object could be probably a better data structure for this kind of task
items = {
ball : "1f7g",
spoon: "2c8d",
pen : "9c3c"
}
console.log(items['ball']); // 1f7g
You may pass array to function as argument and return found value from function:
var item = "spoon"; // from user input
var arr = ["ball", "1f7g", "spoon", "2c8d", "pen", "9c3c"]; //making the array
function findcode(item, arr){
var idx = arr.indexOf(item); //search array for whatever the user input was
if(idx >=0 && idx <= arr.length - 2) { // check index is in array bounds
return arr[i+1]; // return whatever comes next to item
}
return '';
}
document.write(findcode(item, arr)); //write the code, i.e., whatever comes after the item
Answer for 2022
This question came up when I was searching for a modern way to do this. I had been using the technique described in SerzN1's excellent answer without the wraparound (because I don't want that to happen). It winds up being quite a bit of code to make it safe, so I wanted something more modern.
As it turns out, there is a feature that has been available in every major browser since 2016. If someone hasn't updated their browser in six years, that's their loss, right?
ES2015 Array.find()
This function is used for finding a specific element in an array. That's exactly what we want to do here. The only problem is it doesn't maintain state for you, so you can only find a matching element, not the one after (or before, for that matter). To get around that, we use a closure.
Here's the short version:
let trigger = false;
const found = arr.find(element => trigger || (trigger = element === value) && !trigger);
You start with trigger set to false because we need to keep track of when the element is found (if at all). Then we use the Array.find() on the list we should be searching. The single argument to that function is a search function, which we define in-line as a closure so it has access to trigger.
The search function is the tricky part: element => trigger || (trigger = element === query.value) && !trigger. It might be easier to read if I break it apart into a more conventional function just so we can evaluate it. I'll describe what's happening in the comments:
function (element) {
// If trigger is true, that means the previously-evaluated element was the match.
// Therefore, we must be currently evaluating one AFTER it.
// We should match on this one.
if (trigger === true) return true;
// Then we update the value of trigger to the result of comparing the element with the search value
trigger = (element === value)
// Now we `and` it together with its negation in order to make sure
// it always returns false even when the element matches the search value
return trigger && !trigger
}
And there you have it! It only takes two lines of code to get the element after one that matches your query.
Want to see it in action? Here you go:
function showNext() {
const query = document.getElementById('query');
if (query === null) return;
const result = document.getElementById('result');
if (result === null) return;
var arr = ["ball", "1f7g", "spoon", "2c8d", "pen", "9c3c"];
// This is the important part
// Create a boolean flag that will inform the find function when it passes
// the matching element in its search
let trigger = false;
const found = arr.find(element => trigger || (trigger = element === query.value) && !trigger);
// Now `found` is equal to the array element AFTER the one you searched for
// If it is undefined, that means the one you searched for was either the last
// element in the array or it was missing.
result.innerText = `Found ${found ?? 'nothing'}`;
}
<input id="query" />
<input type="submit" value="Display next" onclick="showNext()" />
<div id="result"></div>

Categories