I am pulling data from a JSON array similar to as follows:
"abridged_directors": [{
"name": "Bill",
"id": "742790769",
"characters": ["Flint Lockwood"]
}, {
"name": "Anna",
"id": "162654477",
"characters": ["Sam Sparks"]
}, {
"name": "James",
"id": "162656402",
"characters": ["Tim Lockwood"]
}, {
"name": "Will",
"id": "770670480",
"characters": ["Chester V"]
}, {
"name": "Kristen",
"id": "770792145",
"characters": ["Barb"]
}],
I am using a loop to correctly display the result.
What I am looking for is:
Bill, Ann, James, Will & Kristen
Instead I am getting:
Bill, Ann, James, Will, & Kristen
For the second last name I do not want a comma and the following if else statement should cover that but it doesn't seem to be working?
for (var i = 0;i < data.abridged_directors.length; i++){
if(i != 0 && i == data.abridged_directors.length-1){
// and the position of the character is greater than 0
$(document.body).append('& ' + data.abridged_directors[i].name + '<br>');
}
else if(i != data.abridged_directors.length-1 || i != data.abridged_directors.length-2){
$(document.body).append(data.abridged_directors[i].name + ', ');
}
else
$(document.body).append(data.abridged_directors[i].name + '<br>');
}
I have been looking at this for a good while now and making changes but nothing seems to work. It is possibly something small I'm not seeing so sorry if that turns out to be the case!
I'd like to offer a little cleaner method (imho). Instead of looping through and appending, try joining an array of the names and just replacing the last comma. See code:
var names = [];
for (var i = 0;i < data.abridged_directors.length; i++)
{
names.push(data.abridged_directors[i].name);
}
$(document.body).append(names.join(', ').replace(/(.*),(.*)$/, "$1 & $2"));
Here's a jsfiddle: http://jsfiddle.net/zYdH9/
If you are supporting only IE9+, then you can use a map to do this. The key thing here is a separation of duties. It makes it easier to understand. If you need IE9- support, then you can do this with a for loop. See ezekielDFM's solution.
[editted to include ezekielDFM regex command - much more cleaner solution]
Pseudo Code:
Build your data up first (map command)
Format your data (join command)
Fix formatting (replace command)
Javascript
var string_directors = data.abridged_directors
.map(function(director) {
return director.name;
})
.join(', ')
.replace(/(.*),(.*)$/, '$1 & $2');
The || in the else if should be &&.
i know you get the answer, but i spend some time with solution...kkk
Hope Help someone:
$(json).each(function(i){
$("#result").append(this.name);
if(j-2 == i)
$("#result").append(" & ");
else{
if(!(j-1 == i))
$("#result").append(", ");}
});
http://jsfiddle.net/Z5L4E/
Related
So I'm currently working with the PokeAPI to make a functional Pokedex.
Pokemon can have 1+ types, so I wrote an if statement to deal with that possibility by using the hasOwnProperty selector.
if (data.hasOwnProperty("types[1].type.name")) {
type.innerHTML = "Types: " + data.types[0].type.name + ", " + data.types[1].type.name;
} else {
type.innerHTML = "Type: " + data.types[0].type.name;
}
However, the code doesn't seem to work, and it defaults to the "else" portion of the if statement. Could anyone pinpoint the error in my code? Thanks!
This is an example of what the types object looks like:
"types": [
{
"slot": 2,
"type": {
"url": "https://pokeapi.co/api/v2/type/3/",
"name": "flying"
}
},
{
"slot": 1,
"type": {
"url": "https://pokeapi.co/api/v2/type/10/",
"name": "fire"
}
}
]
What you probably want to use is map, which will convert your array of objects into an array of names quite easily. You can just use this: 'Types: ' + data.types.map(t => t.type.name).join(', ') to get the snippet you want to add to the HTML. This is much simpler than mucking around with lengths or hasOwnProperty.
const data = {"types": [{"slot": 2, "type": {"name": "flying", "url": "https://pokeapi.co/api/v2/type/3/"}}, {"slot": 1, "type": {"name": "fire", "url": "https://pokeapi.co/api/v2/type/10/"}}]}
const run = document.getElementById('run')
const types = document.getElementById('types')
run.addEventListener('click', evt => {
types.innerHTML = 'Types: ' + data.types.map(t => t.type.name).join(', ')
})
<p id="types">(empty until you click "Run")</p>
<button id="run">Run</button>
When you click the button in that snippet, the data is mapped to get the names, and they're joined into your expected string. Now, if your array has three elements, or seventeen, this will continue to work.
Credit to Calvin Nunes,
Instead of using hasOwnProperty() which does not work when there are a variable number of types in the types object, it's easier to just check if there is more than 1 types with if (data.types.length > 1){}.
You're checking a string for data.hasOwnProperty
I think this might work for you:
if (data.hasOwnProperty(types[1].type.name)) {
type.innerHTML = "Types: " + data.types[0].type.name + ", " + data.types[1].type.name;
} else {
type.innerHTML = "Type: " + data.types[0].type.name;
}
This checks the actual value of types[1].type.name and not the string "types[1].type.name". Hope that helps...
I'm trying to using this JavaScript code:
var aStopWords = new Array ("a", "the", "blah"...);
(code to make it run, full code can be found here: https://jsfiddle.net/j2kbpdjr/)
// sText is the body of text that the keywords are being extracted from.
// It's being separated into an array of words.
// remove stop words
for (var m = 0; m < aStopWords.length; m++) {
sText = sText.replace(' ' + aStopWords[m] + ' ', ' ');
}
to get the keywords from a body of text. It works quite well, however, the issue I'm having is that it only seems to iterate through and ignore one instance of the words in the array aStopWords.
So if I have the following body of text:
how are you today? Are you well?
And I put var aStopWords = new Array("are","well") then it seems it will ignore the first instance of are, but still show the second are as a keyword. Whereas it will completely remove / ignore well from the keywords.
If anyone can help ignore all instances of the words in aStopWords from the keywords, I'd greatly appreciate it.
You can easily do this like this.
First, it splits the text into keywords. Then, it goes through all the keywords. While going through, it checks if it is a stopword. If so, it will be ignored. If not, the occurrence number of this keyword in the result object will be increased.
Then, the keywords are in a JavaScript object in the following form:
{ "this": 1, "that": 2 }
Objects are not sortable in JavaScript, but Arrays are. So, a remapping to the following structure is necessary:
[
{ "keyword": "this", "counter": 1 },
{ "keyword": "that", "counter": 2 }
]
Then, the array can be sorted by using the counter attribute. With the slice() function, only the top X values can be extracted from the sorted list.
var stopwords = ["about", "all", "alone", "also", "am", "and", "as", "at", "because", "before", "beside", "besides", "between", "but", "by", "etc", "for", "i", "of", "on", "other", "others", "so", "than", "that", "though", "to", "too", "trough", "until"];
var text = document.getElementById("main").innerHTML;
var keywords = text.split(/[\s\.;:"]+/);
var keywordsAndCounter = {};
for(var i=0; i<keywords.length; i++) {
var keyword = keywords[i];
// keyword is not a stopword and not empty
if(stopwords.indexOf(keyword.toLowerCase()) === -1 && keyword !== "") {
if(!keywordsAndCounter[keyword]) {
keywordsAndCounter[keyword] = 0;
}
keywordsAndCounter[keyword]++;
}
}
// remap from { keyword: counter, keyword2: counter2, ... } to [{ "keyword": keyword, "counter": counter }, {...} ] to make it sortable
var result = [];
var nonStopKeywords = Object.keys(keywordsAndCounter);
for(var i=0; i<nonStopKeywords.length; i++) {
var keyword = nonStopKeywords[i];
result.push({ "keyword": keyword, "counter": keywordsAndCounter[keyword] });
}
// sort the values according to the number of the counter
result.sort(function(a, b) {
return b.counter - a.counter;
});
var topFive = result.slice(0, 5);
console.log(topFive);
<div id="main">This is a test to show that it is all about being between others. I am there until 8 pm event though it will be late. Because it is "cold" outside even though it is besides me.</div>
Essentially what I am trying to achieve here is to check if the Barcode inputted/scanned on the form which is stored in self.trackfile is already in the list of files.
self.files() is an array of arrays, each time the file is added it pushes another array from self.trackfile into self.files(), once all the files have been added into the list they can be 'tracked' and sent back to the server.
I am having trouble getting this to work in IE11 (Compatibility Mode), this works fine in Chrome. I have done some searching around and not found a workaround.
The line var fb = self.files()[x].Barcode(); throws the following error in IE: Object doesn't support property or method 'Barcode'.
If you could help me identify a workaround that would be fantastic!
addFile Script
self.addFile = function () {
var index = 0;
if(index < self.files().length){
var i = 0;
for (x in self.files()){
var fb = self.files()[x].Barcode();
var tb = self.trackfile.Barcode();
if(fb==tb){
i += 1;
}
}
if(i > 0){
alert("Error: File Already Exists in List");
}
else {
self.files.push(new TrackFile(self.trackfile));
}
}
else {
self.files.push(new TrackFile(self.trackfile));
}
}
Example of files()
[
{
"Location": "Location 1",
"TransactionMode": "Send",
"ServicePoint": "Service Point 2",
"Status": "Incomplete / Open",
"Comments": "",
"Barcode": "0123456789",
"BarcodeImageBase64": ""
},
{
"Location": "Location 1",
"TransactionMode": "Send",
"ServicePoint": "ServicePoint 1",
"Status": "Incomplete / Open",
"Comments": "",
"Barcode": "9876543210",
"BarcodeImageBase64": ""
}
]
console.log(self.files()[x]);
Try looping through your array with indexes instead of the for (x in foo) construct. You're probably running into a random property on the prototype of the array that IE adds which obviously wouldn't contain a Barcode.
See: Why is using "for...in" with array iteration a bad idea?
So I figured out how to get around this rather than trying to return a value from a nested array I created an array of just the barcodes:
self.justBarcodes = ko.computed(function() {
var barcodes = ko.utils.arrayMap(this.files(), function(item) {
return item.Barcode();
});
return barcodes.sort();
}, self);
Then I looped through the self.justBarcodes() array to check if the barcode already exists:
for (var x = 0; x < self.justBarcodes().length; x++){
var fb = self.justBarcodes()[x];
var tb = self.trackfile.Barcode();
if(fb==tb){
i += 1;
}
}
Now it works as it should!
See Katana314's answer below
After spending days to figure out how to use regex for BBcode translation I have decided to reinvent the wheel and here I am.
I wrote a script that search for BBcode tags like [b][/b] or any other [] in the text.
I'm looking for an easy solution to compare the tags I found in the text and match them with a table, "array" if you prefer. I want to do it like that because eventually I will use a database to insert and remove BBcodes.
I kinda like the way I do the replacement cause its easy to populate. and there is no need for a Regex.
The replace fonction with it's array:
function bbToHtml(s) {
var p, pairs = [
{ "in": "[b]", "out": '<span style="color:red;">' },
{ "in": "[/b]", "out": '</span>' },
];
for (p in pairs) {
s = s.replace(pairs[p]["in"], pairs[p]["out"]);
}
return s;
}
Now that's fairly simple. What i would like to do is to compare my BBcode with the "in" values.
Lets say i have var BBCode = "[we]";
How do i proceed to see if it matchs one of my array value;
If the result is true then i can just do bbToHtml(BBCode); and if not i skip it or trow an error.
I love ES5 array functions.
function isBB(str) {
return !pairs.every(function(s) {
return s.in !== str;
});
}
every means "return true if this function returns true for all values of this array." This is assuming you are only looking for "[b]" and not "[b] " or any slight variation.
possible solution to your question:
var pairs = [
{ "in": "[b]", "out": '<span style="color:red;">' },
{ "in": "[/b]", "out": '</span>' },
];
function bbToHtml(s)
{
for (var p in pairs) {
s = s.replace(pairs[p]["in"], pairs[p]["out"]);
}
return s;
}
var search = '[we]';
if (pairs[search]) bbToHtml(search);
I am trying to loop over an object and check if a certain property is inside any other certain parts of the object. I am trying to see if the first level.parentSearch is inside any of the other firstlevel.filters array (of objects).
So my object looks like this for example :
var currentfilters = [{
"id": "topics",
"name": "Topics",
"filters": [{
"id": "isSubTopic",
"label": "isSubTopic",
"search": "isSubTopic",
"num": 15
}]
}, {
"id": "isSubTopic",
"name": "isSubTopic",
"parentSearch": "isSubTopic",
"filters": [{
"id": "subtopicFilter",
"label": "subtopicFilter",
"search": "subtopicFilter",
"num": 2
}, {
"id": "subtopicFilter1",
"label": "subtopicFilter1",
"search": "subtopicFilter1",
"num": 2
}]
}, {
"id": "notSubTopic",
"name": "notSubTopic",
"parentSearch": "uniueParentSearch",
"filters": [{
"id": "notSubTopic1",
"label": "notSubTopic1",
"search": "notSubTopic1",
"num": 5
}]
}
];
So what I am trying to achieve is to loop over this object and modify it a little bit (if necessary) and return it. What I am trying to do is check the first level if the .parentSearch property is inside any of the other objects .filter array as a .search property. So in this example isSubTopic would be what I am looking for, because it is inside the Topics filters array.
This is my first time trying these kind of problem, so if I am missing anything please let me know. I figured since I want to return a modified object, it would be good to reduce this object and check inside. The part I am struggling with is the bit that checks if my current parentSearch (in the reduce loop) is in any other objects filter array (under the .search property). I have lodash to mess around with so I have tried both _.find and _.has, but I think I am not approaching this correctly. Any/all input would be greatly appreciated!
Here is what I was trying it with : https://jsfiddle.net/0fttkyey/32/
function checkIfSubtopic(memo, value, key) {
if(value.parentSearch) {
//check if value.parentSearch is in any of the value.filters
console.log("find?", _.find(currentfilters, value.parentSearch));
if(_.find(currentfilters, value.parentSearch)){
console.log("is subtopic?");
} else {
console.log("not sub topic");
}
}
return memo;
}
Interestingly, your currentfilters is not the same in the jsfiddle you provide, so it gives no result.
If I understand correctly, what you try to achieve is:
For each "filter" in currentfilters array, check if parentSearch member is truthy.
If so, loop through all other filters.
For each of these filters, loop through its filters array member.
If the search member of one of the objects in this filters array is equal to parentSearch value, keep the current "filter".
So you have 3 nested loops.
The 2 outer loops iterate over the same currentfilters array, except that the 2nd (inner) one skips the filter which parentSearch value is being searched for.
As soon as parentSearch value is found, break loops 2 and 3 (the 2 inner-most) and go to next item of 1st (outer-most) loop.
Without using lodash and building a new result array (instead of modifying currentfilters array in place which may yield unexpected results), you would have for example:
var i = 0,
j,
ifiltermax = currentfilters.length,
currentParentSearch,
currentFiltersArray,
k,
result = [];
for (; i < ifiltermax; i += 1) { // Loop 1.
currentParentSearch = currentfilters[i].parentSearch;
if (currentParentSearch) { // If `parentSearch` is truthy.
loop_j: for (j = 0; j < ifiltermax; j += 1) { // Loop 2.
if (j != i) { // Skip current filter which `parentSearch` is being searched for.
currentFiltersArray = currentfilters[j].filters;
for (k = 0; k < currentFiltersArray.length; k += 1) { // Loop 3.
if (currentFiltersArray[k].search === currentParentSearch) {
result.push(currentfilters[i]); // Keep current "filter".
console.log("Found " + currentParentSearch + " (from item #" + i + ") in item #" + j + ", filter #" + k);
break loop_j; // Break loops 2 and 3.
}
}
console.log("Did not find " + currentParentSearch + " (from item #" + i + ") in any other filter.");
}
}
}
}
console.log(result);
Updated jsfiddle: https://jsfiddle.net/0fttkyey/71/ (with currentfilters from the question above, instead of the one in jsfiddle version 32).