JavaScript Trivia: Function to combine two Array sets? - javascript

I have two Arrays of object, one has a set of options like this (the left side of the equation):
Id Value
1 Red
2 Blue
3 Green
and another like this (the right side):
Id Value
3 Green
And I need a "left join" (all left products and matching right) like this:
Id Value Selected
1 Red
2 Blue
3 Green X
I would like to create a method with this setup (I put [] to note arrays)
var DisplayArray[] = AllColorsArray[].join(SelectedColors[]);
Is there anything like these already or maybe a JQuery plug-in that does this? It has to be common to provide a selection list with the saved options already checked.
EDIT:
It's really looking for simple SQL like operations on Arrays of objects, but with JavaScript.

use jquery extend, arrays are objects in js:
var object1 = {
apple: 0,
banana: {weight: 52, price: 100},
cherry: 97
};
var object2 = {
banana: {price: 200},
durian: 100
};
$.extend(object1, object2);
In your case will be
var DisplayArray = $.extend(AllColorsArray ,SelectedColors);

You might be interested in LINQ to JavaScript for this sort of thing.

If you set up the arrays in such a way that the indexes are in the same position, you can use a two-dimensional array and then check to see if each dimension is equal. Something like this:
var ary = new Array();
ary[0] = new Array("Red","Blue","Green");
ary[1] = new Array(null, null, "Green");
for (var i in ary[0]) {
alert(ary[0][i] == ary[1][i]); //True if they match, false if not.
}
EDIT
You can manually loop through and check them. You could even throw this in a function where you have to pass the selected and all color arrays. See below for example:
var AllColorsArray = [{"id":1,"value":"Red"},{"id":2,"value":"Blue"},{"id":3,"value":"Green"}];
var SelectedColors = [{"id":3,"value":"Green"}];
for (var ind in AllColorsArray) {
for (var chkind in SelectedColors) {
if (SelectedColors[chkind].id == AllColorsArray[ind].id) {
AllColorsArray[ind] = $.extend(AllColorsArray[ind], SelectedColors[chkind]);
break;
}
}
}
//To test:
for (var i in AllColorsArray) {
alert(AllColorsArray[i].value + ': ' + AllColorsArray[i].selected);
}

Related

How to access object with a randomly chosen name stored in a variable?

I've got 16 objects with names like: aBoard, bBoard, cBoard and so on,
eg. let aBoard = { currentValue: 0, valuesHistory: [], ID: "unitA", borderValues: [1, 0, 0, 1], free: true };
I have an array of corresponding names, randomly chose one of them and change it so it matches the name of one of the objects.
const BOARDARRAY = ["unitA", "unitB", "unitC", "unitD", "unitE", "unitF", "unitG", "unitH", "unitI", "unitJ", "unitK", "unitL", "unitM", "unitN", "unitO", "unitP"];
let randomizer = Math.floor(Math.random()*16);
currentBoardTile = BOARDARRAY[randomizer];
let temp = (currentBoardTile.charAt(currentBoardTile.length -1).toLowerCase());
JSObjectBoardUnit = (temp + "Board");
How to access the object using my JSObjectBoardUnit?
In other words, how to make JS "understand" that I want to treat JSObjectBoardUnit value (string) as a value of the object address?
Eg. Let's day JSObjectBoardUnit = aBoard;
Basically the outcome I want is: aBoard.key1 = JSObjectBoardUnit.key1.
I'd love to use the value stored in JSObjectBoardUnit to access the name of the predefined object aBoard.
I'm not sure to understand well your question but I think this 2 methode could maybe help you.
You can access attribute of a object with a string by using
const obj = {toto: 1};
const name = "toto";
console.log(obj["toto"]); // 1
console.log(obj[name]); // here we use variable and the result is 1
so you could store all yoyr object inside one and do this.
const allBoard = {
"aboard": null, // do not put null use your board
}
console.log(allBoard[(temp + "board")]); // display the temp + board attribute so if temps is equal to a then it will get the aboard
this is what you want, getting object from a string.
But I saw that the aboard also have an id attribute with "unitA"
Instead you could build an array of aboard, bboard ....
and use the Array.find() methode that will return the object that match the condition.
in your case
const myBoardArray = [{ currentValue: 0, valuesHistory: [], ID: "unitA", borderValues: [1, 0, 0, 1], free: true }, ....];
let randomizer = Math.floor(Math.random()*16);
currentBoardTile = BOARDARRAY[randomizer];
myBoardArray.find((board) => board.ID === currentBoardTile);
2 options
Put the boards in a list, and iterate over them with a for loop. In the for loop, use an if statement to see which Id matches the board you want.
let boards = [aBoard , bBoard, cBoard];
boards.forEach(board=> {
if (board.ID == currentBoardTile) {
//do something
}
});
Create a dictionary where the key is the board id and the respective object is the value. Use the board id to get the respective value.
var boards = {
"unitA" : boardA,
"unitB" : boardB,
....
};
currentBoardTile = BOARDARRAY[randomizer];
console.log(currentBoardTile + " : " + boards[currentBoardTile]);

Making a Search Filter with JQuery?

So I have a Table made from some json data...
{
"AKH":{
"name": "Amonkhet",
"code": "AKH"
"cards": [
{
"artist": "Izzy",
"cmc": 3,
"colorIdentity": [
"W"
],
"colors": [
"White"
],
"id": "df3a6e0336684c901358f3ff53ec82ff5d7cdb9d",
"imageName": "gideon of the trials",
"layout": "normal",
"loyalty": 3,
"manaCost": "{1}{W}{W}",
"multiverseid": 426716,
"name": "Gideon of the Trials",
"number": "14",
"rarity": "Mythic Rare",
"subtypes": [
"Gideon"
],
"text": "+1: Until your next turn, prevent all damage target permanent would deal.\n0: Until end of turn, Gideon of the Trials becomes a 4/4 Human Soldier creature with indestructible that's still a planeswalker. Prevent all damage that would be dealt to him this turn.\n0: You get an emblem with \"As long as you control a Gideon planeswalker, you can't lose the game and your opponents can't win the game.\"",
"type": "Planeswalker — Gideon",
"types": [
"Planeswalker"
]
},
The Table row ends up looking like this for each of the cards. at the moment I only Attach the ID, Card name, and Mana Cost to each row
<td><a href="#" onclick="showInfo(this.id)"
id="df3a6e0336684c901358f3ff53ec82ff5d7cdb9d">Gideon of the Trials</a></td>
Now I want to search through these cards. (Keep in mind there are over 17,000 different cards that will be on this list) I can get it to find the things.. But I'm having several different issues... Either it finds them all but doesn't hide the rest of the list, or it hides the whole list and only displays one of the found cards.
So question A... What am I missing to make the search work correctly?
$(document).on('change', 'input[type=checkbox]', function() {
var lis = $('.cardsRow')
$('input[type=checkbox]').filter(':checked').each(function(){
filterKeyB = $(this).attr('id')
filterKeyA = $(this).attr('name')
$.each(json, function(setCode, setListing) {
$.each(setListing.cards,function(cardNum, cardListing){
var x = Object.keys(cardListing)
var y = Object.keys(cardListing).map(function (key){
return cardListing[key]
})
for (i = 0; (i < x.length); i++) {
if(x[i] === filterKeyA){
if (y[i] instanceof Array){
var holder = y[i]
var valueArr =[]
for(var k = 0; k < holder.length; k++){
valueArr = holder.join('|').toLowerCase().split('|')
var foundIt = valueArr.includes(filterKeyB)
}
}else{
var stringy = y[i]
var stringyA= stringy.toLowerCase().replace(/\s/g, '')
if (stringyA === filterKeyB){
var foundIt = true
}
}
if(foundIt === true){
$winner = cardListing.name
for (k = 0; (k < lis.length); k++){
if (lis[k].innerText.indexOf($winner) != -1) {
$(lis[k]).show()
}
}
}
}
}
})
Question B... Since you are already here... Would it be better practice to attach the data that can be searched to the element itself? Maybe just the most searched (Like Name and Mana) and have more advanced queries go through the data again?
I don't understand why the code isn't working or even how it works, it looks like it references some functions that aren't defined in the sample. But I can share with you a really simple/intuitive way to filter stuff, I hope you find it useful.
Native filter method is so useful for what you're trying to do, it takes a callback that takes current element as an arg and returns true or false, if true, the element is included in the new array it produces.
But filter only takes one function, and you have many filters, so let's make a function that combines many filter Fns together into one fn, so you can pass them in all at once:
const combineFilters = (...fns) => val => fns.reduce((prev, curr) => prev || curr(val), false);
OK, how about storing the names of the filter functions as keys in an object so we can reference them using a string? That way we could give each checkbox an ID corresponding to the name of the filter function they are supposed to apply, and makes things really easy to implement (and read):
const filterFns = {
startsWithG(card) {
return card.name[0] === 'G';
},
//etc.
};
OK, time to get the IDs of all the checkboxes that are clicked, then map them into an array of functions.
const filters = $('input[type=checkbox]')
.filter(':checked')
.map((e, i) => $(i).attr('id'))
.get()
.map(fnName => filterFns[fnName])
(Assume the relevant data is stored in a var called...data.) We can use combineFilters combined with filters (array of Fns) to activate all of the relevant filters, then map the resulting array of matching objects into the HTML of your choosing.
const matches = data.cards
.filter(combineFilters(...filters))
.map(card => `<div>${card.name}</div>` );
Then time to update DOM with your matches!
As others have noted, if you need to do any more complicated filtering on objects or arrays, lodash library is your friend!

How to find the position of all array items from a loop

I'm brand new to programming so I apologize if this is a simple question.
I had a unique practice problem that I'm not quite sure how to solve:
I'm dealing with two arrays, both arrays are pulled from HTML elements on the page, one array is representing a bunch of states, and the next array is representing their populations. The point of the problem is to print the name of the states and their less than average populations.
To find and print all of the populations that are less than the average I used this code:
function code6() {
// clears screen.
clr();
// both variables pull data from HTML elements with functions.
var pop = getData2();
var states = getData();
var sum = 0;
for( var i = 0; i < pop.length; i++ ){
sum += parseInt( pop[i], 10 );
var avg = sum/pop.length;
if (pop[i] < avg) {
println(pop[i]);
// other functions used in the code to get data, print, and clear the screen.
function getData() {
var dataSource = getElement("states");
var numberArray = dataSource.value.split('\n');
// Nothing to split returns ['']
if (numberArray[0].length > 0) {
return(numberArray);
} else {
return [];
}
}
// Get the data from second data column
function getData2() {
var dataSource = getElement("pops");
var numberArray = dataSource.value.split('\n');
// Nothing to split returns ['']
if (numberArray[0].length > 0) {
return(numberArray);
} else {
return [];
}
}
// Clear the 'output' text area
function clr() {
var out = getElement("output");
out.value = "";
}
// Print to the 'output' HTML element and ADDS the line break
function println(x) {
if (arguments.length === 0) x = '';
print(x + '\n');
}
Now I just need to know how to get the value of these positions within the array so I can pull out the same positions from my states array and display them both side by side. Both arrays have the identical amount of items.
I hope this makes sense and thanks in advance to anyone who has time to take a look at this.
Best regards,
-E
Its a little hard to tell what you are trying to accomplish, but I guess you are going for something like:
'use strict'
function code6() {
const populations = ['39000000', '28000000', '21000000'];
const stateNames = ['california', 'texas', 'florida'];
const states = populations.map((population, i) => ({
'name': stateNames[i],
'population': Number(population),
}));
const sum = states.reduce((sum, state) => sum + state.population, 0);
const average = sum / populations.length;
states
.filter(state => state.population < average)
.forEach(state => {
const name = state.name;
const population = state.population;
console.log(`state name: ${name}, population: ${population}`);
});
}
// run the code
code6();
// state name: texas, population: 28000000
// state name: florida, population: 21000000
I took the liberty of refactoring your code to be a little more modern (es6) and Idiomatic. I hope its not to confusing for you. Feel free to ask any questions about it.
In short you should use:
'use strict' at the top of your files
const/let
use map/filter/forEach/reduce to iterate lists.
use meaningfull names
, and you should avoid:
classic indexed for-loop
parseInt
, and pretty much never ever use:
var
If your states array is built with corresponding indices to your pop one, like this:
states; //=> ['Alabama', 'Alaska', 'Arizona', ...]
pop; //=> [4863300, 741894, 6931071, ...]
then you could simply update your print statement to take that into account:
if (pop[i] < avg) {
println(state[i] + ': ' + pop[i]);
}
Or some such.
However, working with shared indices can be a very fragile way to use data. Could you rethink your getData and getData2 functions and combine them into one that returns a structure more like this the following?
states; //=> [
// {name: 'Alabama', pop: 4863300}
// {name: 'Alaska', pop: 741894},
// {name: 'Arizona', pop: 6931071},
// ...]
This would entail changes to the code above to work with the pop property of these objects, but it's probably more robust.
If your pop and state looks like:
var state = ['state1', 'state2', ...];
var pop = ['state1 pop', 'state2 pop', ...];
Then first of all, avg is already wrong. sum's value is running along with the loop turning avg's formula into sum as of iteration / array length instead of sum of all pops / array length. You should calculate the average beforehand. array.reduce will be your friend.
var average = pop.reduce(function(sum, val){return sum + val;}, 0) / pop.length;
Now for your filter operation, you can:
Zip up both arrays to one array using array.map.
Filter the resulting array with array.filter.
Finally, loop through the resulting array using array.forEach
Here's sample code:
var states = ['Alabama', 'Alaska'];
var pop = [4863300, 741894];
var average = pop.reduce(function(sum, val){return sum + val;}) / pop.length;
console.log('Average: ' + average);
states.map(function(state, index) {
// Convert 2 arrays to an array of objects representing state info
return { name: state, population: pop[index] };
}).filter(function(stateInfo) {
console.log(stateInfo);
// Filter each item by returning true on items you want to include
return stateInfo.population < average;
}).forEach(function(stateInfo) {
// Lastly, loop through your results
console.log(stateInfo.name + ' has ' + stateInfo.population + ' people');
});

Search array for string - returning -1

I've been reading lots of StackOverflow answers which tell me that, in Javascript, the best way to search an array for a particular string is use indexOf(). I have been trying to make this work for a while now, and I need some help with it.
I am making a shop in a text-adventure game. These are the values I am using:
The array shopCosts:
shopCosts = [20, 25];
The array shopItems:
shopItems = [["Sword", "Shield"]];
I dynamically create radiobuttons by looping through shopItems:
for(var i = 0; i < array.length; i++)
{
// Create the list item:
var item = document.createElement('li');
// Set its contents:
item.appendChild(document.createTextNode(array[i] + " - " + shopCosts[i] + " Gold"));
// Add it to the list:
list.appendChild(item);
var label = document.createElement("label");
var radio = document.createElement("input");
var text = document.createTextNode(array[i]);
radio.type = "radio";
radio.name = "shop";
radio.value = array[i];
radio.onclick = function () { addValue(this.getAttribute("value"), shopCosts, shopItems) }
label.appendChild(radio);
label.appendChild(text);
document.body.appendChild(label);
}
This is the part in question:
radio.onclick = function () { addValue(this.getAttribute("value"), shopCosts, shopItems) }
My logic was basically to assign values to each dynamically created radiobutton, and if one was pressed, get the value (so, the name of the item you wanted to buy) and then search shopItems for that particular string for the index value. Once I had that, I would look in the same "parallel" list shopCosts to find the price.
I used console.log() to see what variables were in play. When I clicked on the radio button, this function is called:
function addValue(nameOfItem, shopCosts, shopItems)
{
var positionOfShopItem = shopItems.indexOf(nameOfItem);
console.log(positionOfShopItem);
console..log(nameOfItem);
console.log(shopItems);
}
Surely, the console.log() would return the position of the named item? To prove to myself I'm not going crazy, here's what the Dev Tools say:
-1
Sword
[Array[2]]
0: "Sword"
1: "Shield"
Sword is clearly in the array, in position 0, so why is indexOf() returning -1?
Any help appreciated!
As I alluded to in my comment, its because shopItems does not contain an array of strings, it contains a single element, where that one element is an array of strings. I suspect your code would work just fine if you removed the extra square braces
var shopItems = ["Sword", "Shield"];
I realize you've already fixed the bug, but I urge you to consider a different approach to the problem. These two principles will not only solve the problem in a cleaner way, but they also give you a new way to think about similar problems in the future:
Never use parallel arrays. Use a single array of objects instead.
In your main loop that appends the items, put the main body of the loop in a function.
If you follow these two ideas you gain several benefits. The code becomes much more straightforward, easier to maintain, and you don't have to do any array lookups at all!
Each shop item is packaged up as a single object in the array, like this:
var shopItems = [
{ name: 'Sword', cost: 20 },
{ name: 'Shield', cost: 25 }
];
So if you have a reference to the shop item as a whole, say in a variable called shopItem, then you automatically have all of its properties available: shopItem.name and shopItem.cost. This lets you also easily add more bits of data to a shop item, e.g.
var shopItems = [
{ name: 'Sword', cost: 20, dangerous: true },
{ name: 'Shield', cost: 25, dangerous: false }
];
and now shopItem.dangerous will give you the appropriate value. All without any array lookups.
Making the main loop body into a function adds a further benefit: Inside that function, its parameters and local variables are preserved each time you call the function (this is called a closure). So now you don't even have to fetch the list item value and look it up - you already have the appropriate shopItem available in the code.
Putting this together, the code might look like this:
var shopItems = [
{ name: 'Sword', cost: 20, dangerous: true },
{ name: 'Shield', cost: 25, dangerous: false }
];
var list = document.getElementById( 'list' );
for( var i = 0; i < shopItems.length; ++i ) {
appendShopItem( shopItems[i] );
}
// Alternatively, you could use .forEach() instead of the for loop.
// This will work in all browsers except very old versions of IE:
// shopItems.forEach( appendShopItem );
function appendShopItem( shopItem ) {
// Create the list item:
var item = document.createElement( 'li' );
// Set its contents:
item.appendChild( document.createTextNode(
shopItem.name + ' - ' + shopItem.cost + ' Gold'
) );
// Add it to the list:
list.appendChild( item );
var label = document.createElement( 'label' );
var radio = document.createElement( 'input' );
var text = document.createTextNode( shopItem.name );
radio.type = 'radio';
radio.name = 'shop';
radio.value = shopItem.name;
radio.onclick = function () {
addValue( shopItem );
};
label.appendChild( radio );
label.appendChild( text );
document.body.appendChild( label );
}
function addValue( shopItem ) {
console.log( shopItem );
alert(
shopItem.name +
' costs ' + shopItem.cost + ' and is ' +
( shopItem.dangerous ? 'dangerous' : 'not dangerous' )
);
}
New fiddle (with a tip of the hat to Jamiec for the original fiddle)
As you can see, this makes the code much easier to understand. If you have a shopItem, you automatically have its name, cost, and any other property you want to add. And most importantly, you never have to keep track of putting your values in the same order in two, three, or even more different arrays.
shopItems is an Array of Arrays. The 0 index of shopItems contains another array which contains:
["Sword", "Shield"]
So when you are trying to find the "Sword" item or "Shield" Item inside of shopItems it is returning -1 because it cannot find either inside of the array.
Change
shopItems = [["Sword", "Shield"]];
To
shopItems = ["Sword", "Shield"];
And that will fix your issue.
I've fixed it!
Removing the double square brackets resulted in this mess. So, as a workaround, I simply added [0] to var positionOfShopItem = shopItems.indexOf(nameOfItem); to get var positionOfShopItem = shopItems[0].indexOf(nameOfItem);
Thanks for everyone's help.

Dictionary equivalent data structure?

I'm working in JavaScript and want to keep a list of set km/mph approximations to hand. (I can't convert programmatically, I'm working with an external API that expects certain values, so it really does have to be a dictionary equivalent.)
Currently I'm using an object:
var KM_MPH = { 10: 16, 12: 20, 15: 24 };
Going from mph to km is pretty easy:
var km = KM_MPH[10];
How do I find mph, given km? Also, is an object the best data structure to use for this sort of thing in JavaScript? I'm more used to Python.
A basic JavaScript object is in fact the best choice here. To find a reverse mapping, you can do:
function mphToKM(val){
for(var km in KM_MPH){
if(KM_MPH[km] === val){
return km;
}
}
return null;
}
Or, if you anticipate having to do a lot of lookups, I would recommend having a secondary JS Object that is the mirror of the first
var mph_km = {};
for(var km in KM_MPH){
mph_km[KM_MPH[km]] = km;
}
// mph_km[16] ==> 10
I don't know if you are in fact doing this for conversion between kilometres per hour to miles per hour... if so, it seems to make more sense to just do the conversion directly instead of relying on a hash mapping of the values.
var conversionRate = 1.609344; // kilometres per mile
function kphToMPH(val){
return val / conversionRate ;
}
function mphToKPH(val){
return val * conversionRate;
}
You can use iterate over all entries to find to find your key
Mostly a dict is used to from key=>value
Alternatively you can have two lists
var km = [];
var mph = [];
with their corresponding indices mapped
This is much closer to a Dictionary data structure, since you can have dozens of elements:
var dictionary = [
{ key: 10, value: 12 },
{ key: 12, value: 20 },
{ key: 15, value: 24 }
];
Then you can also use some JavaScript Framework like jQuery to filter elements:
var element = $.filter(dictionary, function() {
return $(this).attr("key") == 10;
});
alert($(element).attr("value"));
Yes, the JavaScript object is the correct choice.
Create a second object to do the reverse lookup:
var i, MPH_KM = {};
for(i in KM_MPH) MPH_KM[KM_MPH[i]] = i;
var mph = MPH_KM[16];
The dictionary equivalent structure for a javascript object would look like this:
var dictionary = { keys:[], values:[] };
Above structure is an equivalent of
Dictionary(Of Type, Type) **For VB.Net**
Dictionary<Type, Type>) **For C#.Net**
Hope this helps!

Categories

Resources