Iteration through array and objects with properties - javascript

I`m not sure if the title of question is correct. Look at the code:
var trails = new Array(trail1, trail2, trail3, trail4, trail5, trail6, trail7, trail8, trail9, trail10, trail11, trail12, trail13);
var circles = new Array(circle1, circle2, circle3, circle4, circle5, circle6, circle7, circle8, circle9, circle10, circle11, circle12, circle13);
var texts = new Array(text1, text2, text3, text4, text5, text6, text7, text8, text9, text10, text11, text12, text13);
for(var i=0;i<=13;i++) {
$([trails[i].node,circles[i].node,texts[i].node]).qtip({
content: {
text: 'test qtip',
title: {text: 'test', button: 'close'}
},
position: {
target: 'mouse',
adjust: {mouse: false}
},
show: {
event: 'click'
},
style: 'qtip-rounded qtip-shadow qtip-blue',
hide: {
event: 'click '
}
});
}
In this example I`m calling an array elements inside another array, so i'm not sure it's correct, but otherwise .qtip will not show when click on circle[i] or text[i], but only when onclick the trails[i]. There is also a .node property which make this issue much more complicated for beginner. Have any ideas how to improve the code to make it work?

First: Your loop has '<=' where your arrays contain 13 items, the '<=' will iterate 14 times probably causing whatever error your experiencing...
Just to clean up the code a bit...(this part is arbitrary)
var trails = [],circles = [],texts = [], i = 13;
while (i--){
trails[i] = eval('trail'+i);//parses the text to return your js variable
circles[i] = eval('circle'+i);
texts[i] = eval('text'+i);
. . .
/** Continue with whatever else you wish to do inside the loop,
* I just included this bit to show how you can instantiate your
* arrays without having to hard code each of your variables...
* Also, it is possible to use the variables name as a reference
* inside the array like so: trails['trail'+i] = . . .
* that way you can still call each variable by name.
*/
}
And just as a tip, js gets cranky when using the keywork 'new' for arrays, use '[]' instead, you can read why here: W3Schools.com - JS - Best Practices

Related

Select Random Item (shown in pairs) from Array & Remove It, Restart Once Array is Empty

I'm super newbie in coding and I need help to achieve this code.
I'm trying to get a random item (in pairs) from an array and then remove it from this array until user gets to the last item or 60 days have gone from using the service (cookie?)... I have build a script with the help of other questions here in stackoverflow and here is my results so far.
`<script>
var randomizer = document.getElementById("getImgBut");
var dog1 = '/app/wp-content/mediaApp/yo-creo-mi-realidad/01F.jpg';
var dog2 = '/app/wp-content/mediaApp/yo-creo-mi-realidad/01B.jpg';
var dogpics=[dog1,dog2];
var yourPics = [
dogpics,
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/02F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/02B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/03F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/03B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/04F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/04B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/05F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/05B.jpg' ],
[ '/app/wp-content/mediaApp/yo-creo-mi-realidad/06F.jpg', '/app/wp-content/mediaApp/yo-creo-mi-realidad/06B.jpg' ] //This array has 52 cards but I cutted it for example purposes
];
function get_random_number(array){
return Math.floor(Math.random() * array.length |0);
} // here is where I have tried to modify with other scripts like the one in this page https://stackoverflow.com/questions/38882487/select-random-item-from-array-remove-it-restart-once-array-is-empty with no success
randomizer.addEventListener("click", function() {
var rand_number = get_random_number(yourPics);
console.log(rand_number);
document.getElementById('img1').src = yourPics[rand_number][0];
document.getElementById('img2').src = yourPics[rand_number][1];
});
var card = document.querySelector('.card');
card.addEventListener( 'click', function() {
card.classList.toggle('is-flipped');
});
</script>`
Thank you for your help!
I don't fully understand what you mean by "remove in pairs", but I'll answer presuming you mean you wish to remove the image ending in 02F.jpg at the same time as removing the image ending in 02B.jpg, and then 03F.jpg at the same time as 03B.jpg.
The solution to this that I will propose is that we will structure your data a bit differently to begin with. That is, if those images, the "B image" and "F image" are linked, we could keep them in the same `javascript object. This would look like:
var yourPics = [
{
bImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/02F.jpg',
fImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/02B.jpg'
},
{
bImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/03F.jpg',
fImage: '/app/wp-content/mediaApp/yo-creo-mi-realidad/03B.jpg'
}...]
This would then be an array of objects, rather than strings. We can access the bImage property of an object with just
myObject = yourPics[0]
myObject.bImage
We could delete one of those objects those at random via splice.
myRandomlyRemovedObject = yourPics.splice(myIndexToDeleteFrom, 1) would remove 1 object from yourPics at position of myIndexToDeleteFrom, which you presumably would choose randomly. myRandomlyRemovedObject would be assigned to the one object we removed.
I think this object based approach is safer since you will know for a fact that you will removed both matching strings at the same time.

Vaadin Combo-Box: Using value property to pass the index of a selected item

I am trying to discern the index # of the pattern selected in the Combo-box. I need to pass this index value in order for another function to read from a file at the correct location. Essentially, selecting the a pattern in the combobox will let me do a lookup for specifications associated with the selected pattern based on the index. To the best of my knowledge the Vaadin Combobox does not have an index associated with the combobox items, but you are able to pass a different value than the displayed label: https://vaadin.com/docs/-/part/elements/vaadin-combo-box/vaadin-combo-box-basic.html (see: Using Objects as Items). This is solution I am trying to implement, however it gets tricky because I am dynamically populating the combobox items from a JSON file.
The code to dynamically populate the items:
paver = document.querySelector('#paver');
//alert('script executed');
patterns = [];
familyind=y;
$.getJSON('menu.json').done(function(data){
//alert('getJSON request succeeded!');
family = (data.gui[x].family[y].display);
for(ind = 0; ind < data.gui[x].family[y].pattern.length; ind++){
var patternLbl = data.gui[x].family[y].pattern[ind].name;
var patternObj = '{ pattern: { label: "' + patternLbl + '", value: ' + ind + ' } }';
patterns[ind] = patternObj;
}
document.getElementById("cb1").items=patterns;
})
.fail(function(jqXHR, textStatus, errorThrown)
{
alert('getJSON request failed! ' + textStatus);
})
.always(function() { }};
HTML for the ComboBox
<div id="patternSelect">
<template is="dom-bind" id="paver">
<div class="fieldset">
class="patterns" items="[[patterns]]" item-label-path="pattern.label" item-value-path="pattern.value"></vaadin-combo-box>
</div>
</template>
</div>
The output I get when I try to execute this is that the entire constructed string gets assembled into my selection choices. Theoretically, this should not have happened because the item-value-path and item-label-path were specified when declaring the combobox.
Screenshot of Output
It says: { pattern: { label: "A-3 Piece Random", value: 0 } }
WORKING TOWARDS A SOLUTION SECTION:
___________(April 27, 7:00pm)___________
Suggested solution to use,
var patternObj = { pattern: { label: patternLbl, value: ind } };
works fine in displaying labels:
However, I am using a trigger to detect when the value in the combo-box is changed and return the new value. Here is the code for the trigger:
// select template
var paver = document.querySelector('#paver');
// define the ready function callback
paver.ready = function () {
// use the async method to make sure you can access parent/siblings
this.async(function() {
// access sibling or parent elements here
var combobox = document.querySelector('#cb1')
combobox.addEventListener('value-changed', function(event) {
// FOR REFERENCE LOG ERRORS, THIS COMMENT IS ON HTML:215
console.log(event.detail.value);
patval = event.detail.value;
console.log(patval)
// Do stuff with fetched value
});
});
};
I have made the suggested change to using a 'value-changed' trigger. It works very well with two slight issues. First, it returns each of the console log calls twice (not sure why twice). Second, when I select the first combo-box item it returns my values but does not set the label as selected. This is not an issue with the other combo-box items, but the first item needs to be selected twice to have the label set. Please watch this short video for a demonstration: https://youtu.be/yIFc9SiSOUM. This graphical glitch would confuse the user as they would think they did not select a pattern when they know they had. Looking for a solution to make sure the label is set when the first item is selected.
You are setting a currently a String to patternObj while you should be setting an Object.
Try using either var patternObj = JSON.parse('{ pattern: { label: "' + patternLbl + '", value: ' + ind + ' } }'; or even simpler:
var patternObj = { pattern: { label: patternLbl, value: ind } };
Also, I would recommend initializing the patterns = [] inside the done callback to make sure you're not leaving any old items in the patterns when the data changes.

JS: Initialize & push to multi-dimensional array in a single line of code? Create a tree structure

SOLUTION
Thanks to Dave's elegeant solution and answer below here is the solution. Side note: fwiw the additional insight or homework like what Dave provided below is very valuable for noobs. Helps us stretch.
This code will walk an existing JSON tree is for example you wanted to parse each value for whatever reason. It doesn't build but walks. In my case I'm walking and parsing each comment to a richer class:
var db = [], instance = {}, commentCounter = [];
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
_.has(item, 'descendants') ? hydrateComments(item.descendants, item) : 0;
instance = new CommentModel(_.omit(item,['descendants']));
// other parsers go here, example the counter for each level
// parseCounter(comment);
(parent['children'] = (parent['children'] || [])) && item.depth > 0 ?
parent.children.push(instance) :
parent.push(instance);
});
}
hydrateComments(comments, storeComments);
ANGULAR DIRECTIVE
For those who use this code for building a tree I'm including a directive that can help you build a tree using the above mentioned tree.
Please note I've remove a lot of my own code and have not tested this, but I know I spent a ton of time trying to find both the tree and template so hopefully this helps you.
buildTree.$inject = [];
function buildTree() {
link.$inject = ["scope", "elem", "attrs"];
function link(scope, elem, attrs) {
}
CommentController.$inject = ["$scope"];
function CommentController($scope) {
$scope.$watchCollection(function () {
return CommentDataService.getComments();
},
function (newComments, oldValue) {
if (newComments) {
$scope.comments.model = newComments;
}
}, true);
}
return {
restrict: "A",
scope: {
parent: "=cmoParent"
},
template: [
"<div>",
"<script type='text/ng-template'",
"id=" + '"' + "{[{ parent.app_id }]}" + '"' + " >",
"<div class='comment-post-column--content'>",
"<div cmo-comment-post",
"post-article=parent",
"post-comment=comment>",
"</div>",
"</div>",
"<ul ng-if='comment.children'>",
"<li class='comment-post-column--content'",
"ng-include=",
"'" + '"' + "{[{ parent.app_id }]}" + '"' + "'",
"ng-repeat='comment in comment.children",
"track by comment.app_id'>",
"</li>",
"</ul>",
"</script>",
"<ul class='conversation__timeline'>",
"<li class='conversation__post-container'",
"ng-include=",
"'" + '"' + "{[{ parent.app_id }]}" + '"' + "'",
"ng-repeat='comment in comments.model[parent.app_id]",
"track by comment.app_id'>",
"</li>",
"<ul>",
"</div>"
].join(' '),
controller: CommentController,
link: link
}
}
BONUS
I also discover a great trick. How to initialize and populate an array with one line of code. In my case I have a counter method that will count each comment at each level where I've used the tip:
parseCounter: function(comment) {
var depth = comment.depth;
(commentCounter[depth] = (commentCounter[depth] || [])) ? commentCounter[depth]++ : 0;
},
ORIGINAL QUESTION
The code below parses a multi-level array of objects with the purpose of parsing all objects to instances of “CommentModel”, which although simple in this example is much richer object class, but for brevity sake I’m simplified the object/class.
EXISTING STACK EXCHANGE CONTENT:
There is a ton of content on setting multi-dimensional arrays and almost all show the examples such as:
var item[‘level1’][‘level2’] = ‘value’;
or
var item = [];
var item['level1'] = [];
or
var item = new Array([]) // and or objects
but, no examples of something like this:
var item[‘level1’].push(object)
QUESTIONS:
Is there way to initialize a 2 level deep multi-dimensional array and at the same time push to it in one line of code?
1.1 i.e. in my example below of parent[‘children’] I’m forced to check if it exists and if not set it. If I attempt parent[‘children’].push(instance) I obviously get a push on undefined exception. Is there a one liner or a better way to check if property exists and if not? I obviously cannot just set an empty array on parent on every iteration i.e. parent[‘children’] = []; and parent[‘children’] = value wont work
Is it possible to move the initialize and validation to the
CommentModel instance? I ask as I attempted to
CommentModel.prototype['children'] = []; but then all child ('descendants')
objects are added to every object in a proto property called
“children”, which makes sense.
side question - I think my tree iteration code function hydrateComments(items, parent) is concise but is there anything I can do to streamline further with lodash and/or angular? Most example I've seen tend to be verbose and don't really walk the branches.
PLUNKER & CODE
https://plnkr.co/edit/iXnezOplN4hNez14r5Tt?p=preview
var comments = [
{
id: 1,
depth: 0,
subject: 'Subject one'
},
{
id: 2,
depth: 0,
subject: 'Subject two',
descendants: [
{
id: 3,
depth: 1,
subject: 'Subject two dot one'
},
{
id: 4,
depth: 1,
subject: 'Subject two dot two'
}
]
},
{
id: 5,
depth: 0,
subject: 'Subject three',
descendants: [
{
id: 6,
depth: 1,
subject: 'Subject three dot one'
},
{
id: 7,
depth: 1,
subject: 'Subject three dot two',
descendants: [
{
id: 8,
depth: 2,
subject: 'Subject three dot two dot one'
},
{
id: 9,
depth: 2,
subject: 'Subject three dot two dot two'
}
]
}
]
}
];
function hydrateComments(items, parent) {
_.forEach(items, function (item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
if (_.has(instance, 'descendants')) {
hydrateComments(instance.descendants, instance);
}
// we check is parent has a property of children, if not, we set it
// NOTE : 3 lines of code ? is there a more concise approach
if (!_.has(parent, 'children')) {
parent['children'] = [];
}
// if depth id greater than 0, we push all instances of CommentModel of that depth to the
// parent object property 'children'. If depth is 0, we push to root of array
if (item.depth > 0) {
parent.children.push(instance);
} else {
parent.push(instance);
}
})
}
// simple example, but lets assume much richer class / object
function CommentModel(comment) {
this.id = comment.id;
this.depth = comment.depth;
this.subject = comment.subject;
this.descendants = comment.descendants;
}
var output = [];
// init - pass in data and the root array i.e. output
hydrateComments(comments, output);
// Tada - a hydrated multi-level array
console.log('Iteration output for comments : ', output)
To initialise array in single statement you can do as follows
Method 1: (To initialise parent['children']) ANS to Q#1
Plunker for #1: https://plnkr.co/edit/lmkq8mUWaVrclUY2CoMt?p=preview
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
_.has(instance, 'descendants') ? hydrateComments(instance.descendants, instance) : 0;
//Less eff. and less readable then method #2
(parent['children'] = (parent['children'] || [])) && item.depth > 0 ?
parent.children.push(instance) :
parent.push(instance);
});
}
Method 2: (To initialise parent['children']) ANS to Q#2 -- I'd prefer this.
Plunker for #2: https://plnkr.co/edit/zBsF5o9JMb6ETHKOv8eE?p=preview
function CommentModel(comment) {
this.id = comment.id;
this.depth = comment.depth;
this.subject = comment.subject;
this.descendants = comment.descendants;
//Initialise children in constructer itself! :)
this.children = [];
}
function hydrateComments(items, parent) {
_.forEach(items, function(item) {
// create instance of CommentModel form comment. Simply example
var instance = new CommentModel(item);
// if we have descendants then injec the descendants array along with the
// current comment object as we will use the instance as the "relative parent"
_.has(instance, 'descendants') ? hydrateComments(instance.descendants, instance) : 0;
item.depth > 0 ? parent.children.push(instance) : parent.push(instance);
});
}
ANS to Q#3
I feel your code is ok. But if depth increases too much, you might encounter stackoverflow. To get rid of this issue with recursion using trampolines. But if you are sure depth is not
I'd like to quote few lines from above article:
What this graph doesn’t show is that after 30,000 recursive
invocations the browser hung; to the point it had to be forcibly shut
down. Meanwhile the trampoline continued bouncing through hundreds of
thousands of invocations. There are no practical limits to the number
of bounces a trampoline can make.
But only use trampoline is you know that the depth is deep enough to cause stack overflow.
Hope this helps !

Javascript length of array in object

I have a Javascript object defined as follows:
var active = {
waypoints: [],
scenario: []
}
I push to array scenario with:
var myScenario = {
id: terrainId,
text: text
};
active.scenario.push(myScenario);
However I get 0 when:
console.log(active.scenario.length);
So of course I cannot loop through the array content. If I do:
console.log(active.scenario)
I see the array content within Chrome plus the correct array length. I have similar code that defines and works with arrays, but outside of an object definition.
Most grateful for insight into why length is 0.
It works fine:
JSFiddle
var terrainId = 1;
var text = "text";
var active = {
waypoints: [],
scenario: []
}
var myScenario = {
id: terrainId,
text: text
};
active.scenario.push(myScenario);
console.log(active.scenario.length);
Looks like the problem is somewhere else.

How can I call several variables that begin with the same letters and how can I call all variables that contain a certain value?

I need the following two codes
1) A code to select all variables that begin with "example"
2) A code to select all variables that have "true" as value for "available"
example1= {price:1000, size: 1000, available:true}
example2= {price:2000, size: 2000, available:false}
example3= {price:3000, size: 3000, available:true}
example4= {price:4000, size: 4000, available=true}
This is what I want to achieve with code one. As there are a lot of variables I need a quick way of doing it:
var allexampleprices=[example1.price, example2.price, example3.price, example4.price]
With the second code I want to get an array with all the names of the variables that contain the value "false"
Any help appreciated!
All of these are the exact same thing, assuming you're not in a function:
var myVar = 7;
window.myVar = 7;
window["myVar"] = 7;
Therefore, you can access any global variable (a variable defined outside a function) by using the window[ insertString ] method. If you wanted to search through every property on the window object to find one called example, you'd do:
for( var k in window ){
if(/example/.test(k)){
var myExample = window[k];
// Do stuff
}
}
I would HIGHLY recommend against this method, though, for many reasons. To start, it's a horribly bad practice to put anything in the global scope. Variables will start colliding all over the place on big projects. Also, the window object has soooooo many properties that searching through all of them is a horrible performance drain.
Having said all of that, I've devised an example of what you should do, including the helper functions to do it:
var objects =
{
example1:
{
price: 1000,
size: 1000,
available: true
},
example2:
{
price: 2000,
size: 2000,
available: false
},
example3:
{
price: 3000,
size: 3000,
available: true
},
example4:
{
price: 4000,
size: 4000,
available: true
}
}
function filter(obj, comparator){
var list = [];
for(var k in obj){
if(obj.hasOwnProperty(k)){ // fix for IE
if(comparator(obj[k], k, obj)) list.push(obj[k]);
}
}
return list;
}
function isExample(obj, key){
if(/^example/.test( key.toLowerCase() )) return true;
}
function isAvailable(obj){
if(obj.available) return true;
}
/**
* And here's how you use it
*/
var examples = filter(objects, isExample);
var available = filter(objects, isAvailable);
var availableExample = filter(examples, isAvailable);
The filter function returns an array of all of the matching objects.
--- EDIT ---
You want it to say the names of the objects in the console. I'm assuming what you mean is that the console currently shows [object, object, object, object]. There are two ways to do this:
(1) Put the name in the object itself
example1:
{
name: "example1",
price: 1000,
size: 1000,
available: true
}
(2) Capture the name in the filter operation
var names = [];
var examples = filter(objects, function(obj, name){
if(/^example/.test( name.toLowerCase() )){
names.push(name);
return true;
}
});
console.log(names);
I do like below if all variables are in global scope
var passedElements = [];
for(var i = 1, l = 100 /* Maximum number of variable */ ; i < l; i++){
if(window['example' + i]){
var temp = window['example' + i];
if(temp.available === true){
passedElements.push(temp);
}
}/*
else{
// Dont break the loop here, if any variable is missing in between
two variables it will fail. Eg : Example1, Example3.. 2 is missing.
}*/
}
console.log(passedElements);
I hope it will help.
It's seems like follwing line is generated by some logical code:
var example1= {price:1000, size: 1000, available:true}
Why dont you simply store the vaiable names in another array that should give you the solution of Q-1.
Then you can easily travers through all of the vaiables (array) to find the vairables that have "true" as value for "available"

Categories