Checking if two values per key are true in an associative object - javascript

I have an object, written as follows:
var color = "darkred";
var source = "person1"; //this is generated elsewhere and changes
var target = "work23"; //this is generated elsewhere and changes
link = {
color: color,
source: source,
target: target,
value:1
}
This object gets added to an array, links, as follows via a function that is meant to check first to see if it already exists in the array:
var links = [];
function person_linker(link) {
for (var key in links) {
if (links[key].source === link.source && links[key].target === link.target) {
}
else
{
links.push(link);
}
}
}
The problem I'm running into is that it doesn't seem to actually do this check and just adds the link object for however many keys are in links. Everything I've read indicates that writing the if statement is how such a check is made, but most of that information assumes you're only going after one value per key. It's obvious that && is not the way to go, but I've tried separating the two out, doing find, indexOf, and filter statements, and nothing seems to work. The code as is technically returns what I want but because it allows multiple link objects to be added to links it eats up memory when it does the check and creates spurious entries -- with the potential of tens of thousands of extra lines added to the object. What am I doing wrong, here? I'm sure it's a simple fix, but I cannot for the life of me figure out what Javascript wants.

You need to use array filter methods. Here is the example where I use once to check if there is already added link. Also it will be more efficient, because it will skip all of the unnecessary checks after the presented link found in links array.
See more here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
let color = "darkred";
let source = "person1"; //this is generated elsewhere and changes
let target = "work23"; //this is generated elsewhere and changes
let link = {
color: color,
source: source,
target: target,
value: 1,
};
const links = [];
function person_linker(link) {
const linkAlreadyAdded = links.some(presentedLink => {
return (presentedLink.source === link.source) &&
(presentedLink.target === link.target)
});
if (linkAlreadyAdded) {
console.log('Check failed.');
} else {
console.log('Check passed.');
links.push(link);
}
}
console.log(links);
person_linker(link);
console.log(links);
person_linker(link);
console.log(links);

Related

Javascript function to just get all elements from getElementsByClassName

First things first, I've looked in a bunch of seemingly related questions that don't directly relate to my problem:
javascript getElementsByClassName from javascript variable
getElementsByClassName doesn't select all my Navigation elements
Javascript: getElementsByClassName not giving all elements
Javascript document.getElementsByClassName not returning all elements
How to change class for all elements retrieved by document.getElementsByClassName
getElementsByClassName vs. jquery
If there is another question that already addresses my specific problem I apologize and please direct me there.
I'm trying to extract opening and current line data from the following page: https://www.sportsbookreview.com/betting-odds/ncaa-basketball/ and it's only returning data for a certain subset of games. The code is below.
convertHalfLines = stringVal => {
let val
let halfLine = false
if (stringVal.substr(-1) === '\u00BD') {
val = parseFloat(stringVal.slice(0,-1))
halfLine = true
} else {
val = parseFloat(stringVal)
}
return halfLine ? val + (Math.sign(val) * 0.5) : val
}
let games = document.getElementsByClassName("_3A-gC")
let gameInfo = Object.keys(games).map(game => {
let teams = games[game].getElementsByClassName("_3O1Gx")
let currentLines = games[game].getElementsByClassName("_3h0tU")
console.log('currentLines',currentLines)
return {
'homeTeam': teams[1].innerText,
'awayTeam': teams[0].innerText,
'homeWagerPct': parseFloat(currentLines[1].innerText),
'awayWagerPct': parseFloat(currentLines[0].innerText),
'homeOpeningLine': convertHalfLines(currentLines[3].getElementsByClassName('_3Nv_7')[0].innerText),
'awayOpeningLine': convertHalfLines(currentLines[2].getElementsByClassName('_3Nv_7')[0].innerText),
'homeCurrentLine': convertHalfLines(currentLines[5].getElementsByClassName('_3Nv_7')[0].innerText),
'awayCurrentLine': convertHalfLines(currentLines[4].getElementsByClassName('_3Nv_7')[0].innerText),
}
})
The code returns data for a certain set of games, which in and of itself is not consistent. Sometimes it returns data for the first six games, sometimes for the first eight, sometimes less or more than these. Is there something I just don't know about JS that I'm missing or is something else going on?

Completing an item adds two new lines to it, without such a thing in the function it self

I have recreated a todo list on my own, after I've watched a tutorial, and everything works fine, 99% of it at least. Theres only one tiny problem, nothing deal breaking, but it bothers me a lot because I can't find the origin of the issue in the function, even though I've narrowed it down using the debugger. The problem itself is that, whenever I mark an item as "completed" in my todo list, it adds two new empty lines to it (like if someone literally clicked Enter x2, that's how its showcased in the console log). And the issue happens "per item", so its not like if I mark one as completed, it adds two new lines to each item, but just to the one being marked / unmarked. Here's a chunk of the code with the function that I supposedly narrowed it down to:
function completeItem(){
const item = this.parentNode.parentNode;
const parent = item.parentNode;
const id = parent.id;
const value = item.innerText;
if(id === "todo"){
data.todo.splice(data.todo.indexOf(value), 1)
data.completed.push(value);
// Notification.
notification.innerHTML = "<p>Task completed.</p>";
notification.classList.add("notification");
notification.classList.add("notificationCompleted");
setTimeout(() => {
notification.classList.remove("notification");
notification.classList.remove("notificationCompleted");
notification.innerHTML = "";
}, 2000);
} else {
data.completed.splice(data.completed.indexOf(value), 1);
data.todo.push(value);
}
dataObjectUpdate();
console.log(data);
const target = (id === "todo") ? document.getElementById("completed"):document.getElementById("todo");
parent.removeChild(item);
target.insertBefore(item, target.childNodes[0]);
}
Also here's the link to the whole script file on hastebin, if needed:
https://hastebin.com/kuwomiqazu.cs .
Here's a link to a codepen showcasing the issue:
https://codepen.io/pecko95/pen/XBpoGr
Thanks in advance.
textContent returns the text content of all elements, while innerText
returns the content of all elements, except for <script> and <style>
elements.
If you use textContent bug will be fixed.
Replace
const value = item.innerText;
with
const value = item.textContent;

using javascript with a string that contains

Hi I am currently using java to hide certain tabs and fields on my forms depending on the population of dropdowns, for example here is a code that is working:
//Display Transfer tab if it is a transfer application
var ctrlApplicationType = Runner.getControl(pageid, 'ApplicationType');
ctrlApplicationType.on('change', function(e)
{
if (this.getValue() == 2)
{
var tabs = pageObj.getTabs(); tabs.show(2);
}
else
{
var tabs = pageObj.getTabs(); tabs.hide(2);
}
}
);
In the example above the dropdown is fed from a lookup table and returns the primary key INT, hence ==2 works fine.
However I now have a problem when I am trying to get this to work with a checkbox, because the problem is a checkbox can have multiple options.
My lookup table for checkbox has 5 options, so if i ticked option 1, 2 and 3, the field (string) is stored as 1,2,3.
What I need to do is to do change the above code so it returns true if it contains 1, ie
if (1,2,3) contains 1 then true
if (2,3) contains 1 then false.
Any ideas would be much appreciated
Okay, against my better judgement (I'd really like to see you make your own attempt based on the information I've already given you), here you go...
var selectedString = "1,2,3"; // from your code, this is this.getValue()
var selectedArray = selectedString.split(","); // split the string into an array using a comma (,) as the split point
var foundInArray = selectedArray.includes('1'); // foundInArray is now a boolean indicating whether or not the value '1' is one of the values in the array.
if(foundInArray)
{
// do the found action
}
else
{
// do the not found action
}
If you want to compare against integer values instead of string values, that's easy enough too.
var integerArray = selectedArray.map(function(x){ return parseInt(x); });
var foundInArray = integerArray.includes(1);
Finally, all of this can be chained into a one-liner:
if(selectedString.split(",").map(function(x){return parseInt(x);}).includes(1))
{
// do found action
}
else
{
// do not found action
}
To iterate through a fixed list and show/hide each, you can do this...
var possibleTabs = [1,2,3,4,5];
for(n in possibleTabs)
{
if(selectedString.split(",").map(function(x){return parseInt(x);}).includes(n))
{
var tabs = pageObj.getTabs(); tabs.show(n);
}
else
{
var tabs = pageObj.getTabs(); tabs.hide(n);
}
}
This, of course, assumes that there is a relation between the checkbox value and the tabs. If there's not, then you're going to have to list them all out as individual if/elseif/else statements, and that is going to get out of hand really quickly.

slice different arrays and return selected values

I am working on a problem. I do not know the right question to ask in order to solve this problem. I have gotten what seems to be the required results but the verification problem for the solution does not work. I am not sure if I am solving it correctly. Basically I am given an array and I have to filter out elements from that array by slicing certain ingredients.
question: "We only use the elements that the instruction tells us to. So, we need to create a slice of the given array of elements (taken from the beginning) to resemble only the elements we are using. If the instruction doesn't say anything, we only take the first element."
var hammerIngredients = ['iron', 'granite', 'copper'];
var spearIngredients = ['iron', 'granite', 'copper'];
var panIngredients = ['iron', 'granite', 'copper'];
take(hammerIngredients); // returns ['iron']
take(spearIngredients, 2); // returns ['iron', 'granite']
take(panIngredients, 3); // return ['iron', 'granite', 'copper']
"If the instruction says to use more than the elements listed, we use all the elements in the array. If the instruction says to use no elements, we return an empty array."
var plateIngredients = ['iron', 'granite', 'copper'];
take(plateIngredients, 0); // returns []
So I have tried to do the program and I have done the following. It appears to work, but when I try to verify it I get that it is invalid.
function take(ingredients, slicer) {
if (arguments.length === 1) {
slicer = 1;
}
if (ingredients === hammerIngredients){
return ingredients.slice(0, slicer);
} else if(ingredients === spearIngredients) {
return ingredients.slice(0,slicer);
} else if (ingredients === panIngredients) {
return ingredients.slice(0,slicer);
} else if (ingredients === plateIngredients) {
return ingredients.slice(0,slicer)
} else {
return;
}
}
And I have no idea why. Help please!
you have no logic for if the slicer parameter is 0, in which case you need to return an empty array.
Put this line in there and it should work, based on the requirements you gave:
if (slicer === 0) {
return [];
}
You code currently only works if one of those three exact arrays are used. Does the verification code create and use only those arrays?
Your code does not need to be tied to existing ingredient arrays. After setting the default slicer value you can just:
return ingredients.slice(0,slicer);

Build a switch based on array

I want to create a Javascript switch based on an array I'm creating from a query string. I'm not sure how to proceed.
Let's say I have an array like this :
var myArray = ("#general","#controlpanel","#database");
I want to create this...
switch(target){
case "#general":
$("#general").show();
$("#controlpanel, #database").hide();
break;
case "#controlpanel":
$("#controlpanel").show();
$("#general, #database").hide();
break;
case "#database":
$("#database").show();
$("#general, #controlpanel").hide();
break;
}
myArray could contain any amount of elements so I want the switch to be created dynamically based on length of the array. The default case would always be the first option.
The array is created from a location.href with a regex to extract only what I need.
Thanks alot!
#Michael has the correct general answer, but here's a far simpler way to accomplish the same goal:
// Once, at startup
var $items = $("#general,#controlpanel,#database");
// When it's time to show a target
$items.hide(); // Hide 'em all, even the one to show
$(target).show(); // OK, now show just that one
If you really only have an array of selectors then you can create a jQuery collection of them via:
var items = ["#general","#controlpanel","#database"];
var $items = $(items.join(','));
Oh, and "Thanks, Alot!" :)
I think you want an object. Just define keys with the names of your elements to match, and functions as the values. e.g.
var switchObj = {
"#general": function () {
$("#general").show();
$("#controlpanel, #database").hide();
},
"#controlpanel": function () {
$("#controlpanel").show();
$("#general, #database").hide();
},
"#database": function () {
$("#database").show();
$("#general, #controlpanel").hide();
}
}
Then you can just call the one you want with
switchObj[target]();
Granted: this solution is better if you need to do explicitly different things with each element, and unlike the other answers it focused on what the explicit subject of the question was, rather than what the OP was trying to accomplish with said data structure.
Rather than a switch, you need two statements: first, to show the selected target, and second to hide all others.
// Array as a jQuery object instead of a regular array of strings
var myArray = $("#general,#controlpanel,#database");
$(target).show();
// Loop over jQuery list and unless the id of the current
// list node matches the value of target, hide it.
myArray.each(function() {
// Test if the current node's doesn't matche #target
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
});
In fact, the first statement can be incorporated into the loop.
var myArray = $("#general,#controlpanel,#database");
myArray.each(function() {
if ('#' + $(this).prop('id') !== target) {
$(this).hide();
}
else {
$(this).show();
}
});
Perhaps you're looking for something like this? Populate myArray with the elements you're using.
var myArray = ["#general","#controlpanel","#database"];
var clone = myArray.slice(0); // Clone the array
var test;
if ((test = clone.indexOf(target)) !== -1) {
$(target).show();
clone.splice(test,1); // Remove the one we've picked up
$(clone.join(',')).hide(); // Hide the remaining array elements
}
here you dont need to explicitly list all the cases, just let the array define them. make sure though, that target exists in the array, otherwise you'll need an if statement.
var target = "#controlpanel";
var items = ["#general","#controlpanel","#database"];
items.splice($.inArray(target, items), 1);
$(target).show();
$(items.join(",")).hide();
items.push(target);

Categories

Resources