search within an array in javascript - javascript

I've been trying for a while now to search within an array, I've looked at all the other questions that even somewhat resemble mine and nothing works, so I'm asking for any help you can give now..
I have an array with a more complex insides than a simple string array
var elementDefns = [
{"element":"water", "combos": {"air":"steam", "earth":"sand"} },
{"element":"fire", "combos": {"earth":"lava", "air":"energy"} },
{"element":"air", "combos": {"water":"steam", "earth":"dust"} },
{"element":"earth", "combos": {"water":"swamp", "fire":"lava"} },
];
Two elements are picked (by the users) which are combined to create new elements. I'd like to search through the elements for any combos that can be made. Ideally, I'd want to use Array.prototype.find, although I can't figure out how to use polyfills correctly and i'm unsure if i'm writing it correctly, so it continues to not work
var elementOne = $("#board img:first-child").attr('id');
var elementTwo = $("#board img:last-child").attr('id');
function findElement(element) {
return elementDefns.element === elementOne;
}
board is the id div where the element cards go to once clicked. I also tried a loop
for (var i=0, tot=elementDefns.length; i < tot; i++) {
var indexHelp = elementDefns[i].element;
var find = indexHelp.search(elementOne);
console.log(find);
}
I'm trying to post a question that's not too long, but I'm sure there's lots more about my code i need to adjust in order to do this. I guess I'm just asking if there's something obvious you think i could work on. I've looked at most of the answers on this site to similar problems but its all just going horribly wrong so any other support would be greatly appreciated..

I have an array with a more complex insides than a simple string array
Yes, but why? Get rid of the extra layers and this is trivial
var e1 = "water";
var e2 = "air";
var elementDefns = {
"water": {"combos": {"air":"steam", "earth":"sand"} },
"fire": {"combos": {"earth":"lava", "air":"energy"} },
"air": {"combos": {"water":"steam", "earth":"dust"} },
"earth": {"combos": {"water":"swamp", "fire":"lava"} },
};
elementDefns[e1].combos[e2] = > "steam"

If you want to keep your data-structure, you can filter through it like this:
var matches = elementDefns
.filter(e => e.element == first && e.combos[second] !== null)
.map(e => e.combos[second]);
The first row filters out all matches, and the secon maps it over to the actual match-string (element name). The find() you speak of just returns the first value that matches, and i guess you want all, so that would be the filter() method.

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?

Create a variable with two arrays made from two .map() looking for match or intersection

The point is I'm not an expert with JS so excuse me if my code is a big mistake, in fact I'm just training to learn making a huge "Frankenstein" from a research around the web.
I want to take two set or lists of values input by the user and compare them looking for matches with js and jquery.
First, I take the values from two groups of inputs (diferenced by class) with .map(), this to have the values to create the arrays.
var x = $(".row_a").map(function() {
return this.value;
}).get();
var y = $(".row_b").map(function() {
return this.value;
}).get();
Then I'm traying to create one variable that contain the two arryas separatly (here Is when think I have the problem) if I "hardcode" the arrays the next part of the script works as expected, but if I use the the first two variables made above, the script just crash.
var arrays = [
[x],
[y]
];
In the third part (I really don't understand this part of the code in deep) I run the script that compare the two arrys on the second variable and then append a paragraph with the result.
var result = arrays.shift().reduce(function(res, v) {
if (res.indexOf(v) === -1 && arrays.every(function(a) {
return a.indexOf(v) !== -1;
})) res.push(v);
return res;
}, []);
$(".match").append("<div>Numbers " + result + " match in both lists</div>");
Somebody can help me to undersnatd whats wrong or giveme a clue or link that can help?
curious: if I use the same variable twice in the variable that contain the arrays, the script works and finds four matches (I think is right becouse compare the same array)
DEMO
--
EDITED:
Thanks to #KarlReid and #Potorr. I didn't know the intersection concept in javascript, so now I know a better way to achive the result. I'll read more about it to try understand the answer in deep.
Thanks to #Alexandru for letting me know the sintax error, it'll be very basic and usefull henceforth.
Final Result: DEMO
(I'll be edit the tittle of the post to try improve the search for others with the same question in the future.)
This works, explanation in-line:
$(".action").click(function() {
var x = $(".row_a").map(function() {
return this.value;
}).get()
var y = $(".row_b").map(function() {
return this.value;
}).get()
// this is not really needed, if you want it:
// x, y are already arrays, so you don't need to wrap them with []
// var arrays = [
// x,
// y
//];
// intersection, adapted from: https://stackoverflow.com/questions/1885557/simplest-code-for-array-intersection-in-javascript
// if you use the "arrays" variable, then change x to arrays[0] and y to arrays[1]
//var intersection = x.filter(function(n) {
// return y.indexOf(n) !== -1;
//});
// the intersection function posted by #KarlReid is actually better and faster:
var intersection = x.filter(Set.prototype.has, new Set(y));
// create the result string
var result = intersection.join(',')
$(".match").append("<div>Numbers " + result + " match in both lists</div>");
});
Edit: Changed intersection function to the one posted by #KarlReid
Although better solutions exists, as pointed out by comments the Potorr's answer, the only problem with the code posted is this:
var arrays = [
[x],
[y]
];
x and y are already arrays, so you don't need to wrap them with [ ]. The code works if you simply replace the above code with:
var arrays = [
x,
y
];
Updated demo

How to avoid using filter twice against the same set of elements

I'm trying to separate two types of inputs into their own jQuery wrapped sets as they need to be processed differently depending on whether the id contain '-add-new-' or not. I know I could do this using filter twice as follows:
var seriesTabInputs = $msSeriesTabs.find('input').filter(function() {
return $(this).attr('id').indexOf('-add-new-') == -1;
});
var addNewTabInputs = $msSeriesTabs.find('input').filter(function() {
return $(this).attr('id').indexOf('-add-new-') >= 0;
});
However filtering twice seems inefficient to me as I know it will require a second loop. Is there a way to avoid this?
Try like below:
var addNewTabInputs = $msSeriesTabs.find('input[id*="-add-new-"]');
var seriesTabInputs = $msSeriesTabs.find('input[id]:not([id*="-add-new-"])');
OR
var addNewTabInputs = $msSeriesTabs.find('input[id*="-add-new-"]');
var seriesTabInputs = $msSeriesTabs.find('input[id]').not(addNewTabInputs);
Just to offer an alternative to using specific selectors, you could iterate through the jQuery set and build the two collections as you go. I don't know that this would be any faster due to the different operations applied to the collections.
var $inputs = $msSeriesTabs.find('input');
var seriesTabInputs = [];
var addNewTabInputs = [];
for (var i = 0; i < $inputs.length ; i += 1)
{
var input = $($inputs[i]);
if ( $(input).attr('id').indexOf('-add-new-') >= 0 )
{ addNewTabInputs.push(input); }
else
{ seriesTabInputs.push(input); }
}
seriesTabInputs = $(seriesTabInputs);
addNewTabInputs = $(addNewTabInputs);
Avoiding filtering twice may not be so crucial unless you are dealing with an enormous amount of elements. Furthermore there is something to be said for the consistency of the code when you filter twice.
That being said there is a way to avoid filtering twice and it may even be instructional; below is some code that can be used to achieve this.
First, we create an empty wrapped set that can be added to, this is achieved by var seriesTabInputs = $(false); Please see this write-up for more information.
Then inside of the filter, we conditionally add to seriesTabInputs but note the way in which we do it: we continually re-assign with seriesTabInputs = seriesTabInputs.add($(this)); If instead you merely call seriesTabInputs.add($(this)) without assigning to seriesTabInput you will wind up with an empty array in the end. Please see the jQuery docs for .add() which gives a similar incorrect example and states that such usage "will not save the added elements, because the .add() method creates a new set".
var seriesTabInputs = $(false);
var addNewTabInputs = $msSeriesTabs.find('input').filter(function() {
if ($(this).attr('id').indexOf('-add-new') >= 0) {
return true;
}
else {
seriesTabInputs = seriesTabInputs.add($(this));
}
});

Searching for an element in a Json object

I have an array of object (JSONized). Something like this :
popUpList = [{
"id":1,
"Name":"My Pop Up",
"VideoUrl":"www.xyz.com/pqr",
"AddToBasketUrl":"www.abc.com?addtoBaketid=1",
"addedToBasket": true
},
{
"id":2,
"Name":"My 2nd Pop Up",
"VideoUrl":"www.xyz.com/mno",
"AddToBasketUrl":"www.abc.com?addtoBaketid=2",
"addedToBasket": false
}]
My situation is a clip can be either added from he pop up or the main page. So, I need to edit the JSON object when something is added to basket from the page.
I tried using $.inArray() and similar methods. i reckon either I am not doing it the right way or missing something. Or, this cannot work for JSON objects and I have to loop through every object.
Any help will be appreciated.
Array.indexOf (what $.inArray is) does need the element to search for and returns its index.
If you need to search for an element you don't know before, you will need to loop manually (Libs like Underscore have helpers):
var idToSearchFor = …;
for (var i=0; i<popUpList.length; i++)
if (popUpList[i].id == idToSearchFor) {
// do something
}
If you want to build an index for faster accessing popups, you can do that as well. It also has the advantage of being unambiguous (only one element per id):
var popUpsById = {};
for (var i=0; i<popUpList.length; i++)
popUpsById[popUpList[i].id] = popUpList[i];
if (idToSearchFor in popUpsById)
// do something with popUpsById[idToSearchFor]
else
// create one?
I'm not quite sure what you want to search explicitely, you can access each value in your object by using:
vat id = "1";
// this given example wont work for you bec. of your structure
// but its all about the idea.
var objectOne = yourJsonObject[id];
// You can also append them
var myValue = yourJsonObject.address.zip;
And similiar on any other item of the fetched first object.
For that I would create a custom search function which would look like that:
$.each(popUpList, function(i, v) {
var entryYouWantToFind = "addedToBasket";
if(v[entryYouWantToFind])
{
// do your stuff here.
}
}
});
I hope I could give you the hint.

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