Comparing an array to text in JavaScript - javascript

How could one go about checking all the document's elements' data attributes, and if the contents of an attribute match a value from in an array, do something?
I have an array that I'm creating by grabbing the name and value attributes from text inputs. I want to, for example, compare the value of name "X" to the value of a span with a data-attribute of "X".
The code I currently have is this:
bulletinPreview.prototype.updatePreview = function () {
var dataValues = new Array();
$(".edit-bulletin input[type=text]").each(function () {
var $this = $(this),
dataName = $this.attr("name"),
dataValue = $this.attr("value");
dataValues.push({
"name": dataName,
"value": dataValue
});
});
$(".bulletin-preview .editable").each(function () {
var $this = $(this);
if ($.inArray($this.data("title"), dataValues.name !== -1)) {
$this.text(dataValues[1].value);
}
});
};
It replaces the text for ALL elements with a data-title of title the second value (because I'm explicitly saying dataValues[1] just to see something work). How do I take this and make it modular, so that it just runs through all the elements, runs through the array, and if two values match, do something?

I think you're looking for this:
var $preview = $(".bulletin-preview");
$.each(dataValues, function(index, entry) {
var $edit = $preview.find('.editable[data-title="' + entry.name + '"]');
$edit.text(entry.value); // No-op if no match
});
That assumes none of the names contains double quotes.
If the repeated searches through $preview bother you, you can do one search and make yourself a map:
var map = {};
$(".bulletin-preview .editable").each(function() {
map[this.getAttribute("data-title")] = this;
});
$.each(dataValues, function(index, entry) {
var edit = map[entry.name];
if (edit) {
$(edit).text(entry.value);
}
});

Related

Loop, get unique values and update

I am doing the below to get certain nodes from a treeview followed by getting text from those nodes, filtering text to remove unique and then appending custom image to the duplicate nodes.
For this I am having to loop 4 times. Is there is a simpler way of doing this? I am worried about it's performance for large amount of data.
//Append duplicate item nodes with custom icon
function addRemoveForDuplicateItems() {
var treeView = $('#MyTree').data('t-TreeView li.t-item');
var myNodes = $("span.my-node", treeView);
var myNames = [];
$(myNodes).each(function () {
myNames.push($(this).text());
});
var duplicateItems = getDuplicateItems(myNames);
$(myNodes).each(function () {
if (duplicateItems.indexOf($(this).text()) > -1) {
$(this).parent().append(("<span class='remove'></span>"));
}
});
}
//Get all duplicate items removing unique ones
//Input [1,2,3,3,2,2,4,5,6,7,7,7,7] output [2,3,3,2,2,7,7,7,7]
function getDuplicateItems(myNames) {
var duplicateItems = [], itemOccurance = {};
for (var i = 0; i < myNames.length; i++) {
var dept = myNames[i];
itemOccurance[dept] = itemOccurance[dept] >= 1 ? itemOccurance[dept] + 1 : 1;
}
for (var item in itemOccurance) {
if (itemOccurance[item] > 1)
duplicateItems.push(item);
}
return duplicateItems;
}
If I understand correctly, the whole point here is simply to mark duplicates, right? You ought to be able to do this in two simpler passes:
var seen = {};
var SEEN_ONCE = 1;
var SEEN_DUPE = 2;
// First pass, build object
myNodes.each(function () {
var name = $(this).text();
var seen = seen[name];
seen[name] = seen ? SEEN_DUPE : SEEN_ONCE;
});
// Second pass, append node
myNodes.each(function () {
var name = $(this).text();
if (seen[name] === SEEN_DUPE) {
$(this).parent().append("<span class='remove'></span>");
}
});
If you're actually concerned about performance, note that iterating over DOM elements is much more of a performance concern than iterating over an in-memory array. The $(myNodes).each(...) calls are likely significantly more expensive than iteration over a comparable array of the same length. You can gain some efficiencies from this, by running the second pass over an array and only accessing DOM nodes as necessary:
var names = [];
var seen = {};
var SEEN_ONCE = 1;
var SEEN_DUPE = 2;
// First pass, build object
myNodes.each(function () {
var name = $(this).text();
var seen = seen[name];
names.push(name);
seen[name] = seen ? SEEN_DUPE : SEEN_ONCE;
});
// Second pass, append node only for dupes
names.forEach(function(name, index) {
if (seen[name] === SEEN_DUPE) {
myNodes.eq(index).parent()
.append("<span class='remove'></span>");
}
});
The approach of this code is to go through the list, using the property name to indicate whether the value is in the array. After execution, itemOccurance will have a list of all the names, no duplicates.
var i, dept, itemOccurance = {};
for (i = 0; i < myNames.length; i++) {
dept = myNames[i];
if (typeof itemOccurance[dept] == undefined) {
itemOccurance[dept] = true;
}
}
If you must keep getDuplicateItems() as a separate, generic function, then the first loop (from myNodes to myNames) and last loop (iterate myNodes again to add the span) would be unavoidable. But I am curious. According to your code, duplicateItems can just be a set! This would help simplify the 2 loops inside getDuplicateItems(). #user2182349's answer just needs one modification: add a return, e.g. return Object.keys(itemOccurance).
If you're only concerned with ascertaining duplication and not particularly concerned about the exact number of occurrences then you could consider refactoring your getDuplicateItems() function like so:
function getDuplicateItems(myNames) {
var duplicateItems = [], clonedArray = myNames.concat(), i, dept;
for(i=0;i<clonedArray.length;i+=1){
dept = clonedArray[i];
if(clonedArray.indexOf(dept) !== clonedArray.lastIndexOf(dept)){
if(duplicateItems.indexOf(dept) === -1){
duplicateItems.push(dept);
}
/* Remove duplicate found by lastIndexOf, since we've already established that it's a duplicate */
clonedArray.splice(clonedArray.lastIndexOf(dept), 1);
}
}
return duplicateItems;
}

jQuery.data only saves data from the last Element

i am trying to use jQuery.data() and save an Object to my HTML-Elements. Everytime i add an list-Element to my unordered List it only saves the last object to the specific li-Element. Every other li-Elements saved data gets thrown away!
I've built a little Example. JSBin-Example
On the left, i create a List with an Object saved to it. On the right i am trying to show the data related to the Object.
Why does it only show the Object related to the last HTML-Element?
Working example:
JSBin-Example
That's because you are modifying innerHTML property of the wrapper element. What happens is in each iteration the elements are regenerated, the current elements are removed and the new elements don't have any stored data. Using innerHTML property is the worst way of modifying element contents. You just need to create a li element and append it to the wrapper element:
var random = 0;
// var testObject = [];
function addNewItem(){
random += 1;
var id = "testId" + random;
var text = "This is my " + random + ". text";
var data = {id: id, text: text};
// testObject.push(data);
// You can pass an object as the second argument
// to jQuery constructor and it calls the
// corresponding methods as setter
$('<li></li>', {
text: text + JSON.stringify(data),
id: id,
data: data
}).appendTo('#listId');
}
// bind and trigger click event
$("#add").on('click', addNewItem).click();
I changed
for(var i = 0; i < testObject.length; i++){
var listItem = "";
var id = testObject[i].id;
listItem += liStart + id + liStart2;
listItem += testObject[i].text;
listItem += liEnd;
unorderedList.innerHTML += listItem;
$("#"+id).data(testObject[i]);
}
to this in your updatelist function
//for(var i = 0; i < testObject.length; i++){
var id = testObject[testObject.length-1].id;
listItems += liStart + id+"savedData" + liStart2;
listItems += JSON.stringify($("#"+id).data());
listItems += liEnd;
//}
savedData.innerHTML += listItems;
and it fixed the issue
To help you understand my comment on the question I thought it best I'd give an example of what I meant.
I didn't have enough time to fully go through the solution but wanted to give an example of what I'd call more readable code.
I've added all variables at the top of the function. This will allow you to read and find items much quicker if you needed to alter them.
I've also merged a lot of the string values that you had into an object, namely the li element.
I've never used $.data() as an object before so wasn't really aware how I could use it to set the values in the updateSavedData() $('li'), although the console.log() does show the correct key / values.
$(document).ready(function(){
var uID = 0;
var testObject = [];
var unorderedList = $("#listId");
var savedList = $("#savedData");
var TOL = 0; //TestObjectLength
var textTemplate = "This is my [0] text!";
function addNewItem(){
uID++;
testObject.push({id: uID, text: textTemplate.replace("[0]", uID)});
TOL = testObject.length-1;
updateList();
}
function updateList(){
var li = $('<li>', { id: testObject[TOL].id, data: testObject[TOL], text: testObject[TOL].text });
li.appendTo(unorderedList);
updateSavedData(li.data());
}
function updateSavedData(li){
console.log(JSON.stringify(li));
$('<li>', JSON.stringify(li)).appendTo(savedList);
}
addNewItem();
$("#add").on('click', addNewItem);
});
Working Example
http://jsbin.com/ralizazahe/1/edit?js,console,output
Anyone that wants to progress on that please do as I'd also like to see how this could be progressed more.
Update
Taken it a step more and refactored to this
$(document).ready(function(){
var $displayList = $("#listId");
var $savedList = $("#savedData");
var textTemplate = "This is my {0} text!";
var uID = 0; //Unique ID
var data = { id: null, text: null }; //Gives a reference
function init(){
uID++;
data = { id: uID, text: textTemplate.replace("{0}", uID) };
}
function addNewItem(){
init();
$('<li>', data).appendTo($displayList);
updateSavedData(data);
}
function updateSavedData(li){
$('<li>', li).appendTo($savedList);
}
addNewItem();
$("#add").on('click', addNewItem);
});
http://jsbin.com/bajekagoli/1/edit?js,console,output

jQuery get json data, match the ids, then append the custom field value

I am trying to retrieve json data from zendesk, then compare the value of the ids in each ticket that is retrieved from the server with the id inside my table data. If the ids match, then I want to retrieve the custom field value inside the ticket with the matching id, and append it to the table data's that i have named with the product id. The idea is to target each "td/td",and automatically insert the custom field(product id) value when a new "td/td" tag is created. Please help :) The Object 99 at the top is the ticket, the id :175 is the id I am trying to match. the custom_field array[10] value is what I want to append and display. The issue I am having is that the data is not displaying after I append it. I think this has something to do with how I am accessing the data, comparing the ids, then appending the custom field. The array value is returning a -1 instead of comparing the IDs. This is the problem.
//first lets get some json data!
var getjson = $.getJSON("/api/v2/tickets.json)",function (result) {
$.each(result, function(i, field) {
console.log('data',field);
// now lets take the text id for each td and assign it as the id.
$(".sort > tr > td:nth-child(1)").each(function(){
var textid = $(this).text();
$(this).attr('id', textid);
// if the ids match, give me the value of the custom field that is inside the array of the matching id.
var arr = [field];
var arrayvalues = $.inArray('id', arr);
if ($(this).attr('id') == arrayvalues){
/* if the ids match, give me the value of the
custom field that is inside the array of the matching id. */
var returns = $.inArray('value',arr.custom_fields[i]);
// now for each td lets append the value of the custom field.
$(".sort > tr > td:nth-child(7)").each(function () {
$(this).append(returns);
$(this).attr('id','product');
})
}
})
});
See $.inArray usage described at documentation
jQuery.inArray( value, array [, fromIndex ] )
value
Type: Anything The value to search for.
array
Type: Array An array through which to
search. fromIndex Type: Number The index of the array at which to
begin the search. The default is 0, which will search the whole array.
Note, also $.inArray()
Returns: Number
Not certain about return value from $.getJSON ? full json not appear at original post ? , though see
$.getJSON("/api/v2/tickets.json}"
right bracket before closing double-quotation mark at url ?
Try changing js at
var arrayvalues = $.inArray(field).id;
and
var returns = return $.inArray(field).custom_fields('value');
to usage described at documentation, e.g.,
var arr = ["a", "b", "c"];
console.log(
$.inArray("a", arr) // `0`
, $.inArray("b", arr) !== -1 // `true`
, $.inArray("c", arr) === 3 // `false`
);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
I figured it out after days of knocking my head against the wall. The following code works perfectly.
var Obj1 = {};
var Obj2 = {};
var Obj3 = {};
var urlstring = "/api/v2/tickets/"+id+".json)";
$.getJSON(urlstring,function (result) {
$.each(result, function(i, value) {
Obj1[result[i].id] = result[i].fields[10].value;
Obj2[result[i].id] = result[i].fields[13].value;
Obj3[result[i].id] = result[i].fields[7].value;
console.log('Obj1', Obj1);
console.log('Obj2', Obj2);
console.log('Obj3', Obj3);
});
$.each(Obj1, function(key, element) {
$(".sort > tr").each(function(){
if($(this).attr('id') == key){
$(this).children('td:nth-child(7)').append(element);
}
});//end tr each
}); //end obj1 each
$.each(Obj2, function(key, element) {
$(".sort > tr").each(function(){
if($(this).attr('id') == key){
$(this).children('td:nth-child(8)').append(element);
}
});//end tr each
}); //end obj2 each
$.each(Obj3, function(key, element) {
$(".sort > tr").each(function(){
if($(this).attr('id') == key){
$(this).children('td:nth-child(6)').append(element);
}
});//end tr each
}); //end obj3 each
});//end json

Select Part of a Attribute - JQuery

I need to get the number only from an attribute (The number is dynamic). The button/anchor looks like this:
Delete Dish
The part I need to dissect is this bit 'bc_inventorybundle_menu_product_0' I only want the number, for use in another function (Delete a LI with an ID of menuitem0_dish)
The code I use for selecting ID's elsewhere is:
function getNum(element, attrPrefix) {
//set prefix, get number
var prefix = attrPrefix;
var num = element.attr("id").substring((prefix.length));
return num;
}
It works great on ID's but I cant seem to get it to work for Attributes instead of ID's
So User clicks delete button bc_inventorybundle_menu_product_0 then jQuery removes the < li id="menuitem0_dish">
I can't add an ID to the button so I have to use the attribute of the button. As I'm sure you can tell I'm a complete noob when it comes to JS/JQuery.
EDIT
Having read all the answers I feel I may need to elaborate a little.
I think the biggest issue is registering when the Button/Anchor is clicked.
What I currently have is this, which I know must be wrong:
$(document).on('click', 'data("field")', function(event) {
deleteDish(this);
});
function getbutNum(element, attrPrefix) {
//set prefix, get number
var prefix = attrPrefix;
var butnum = element.data("field").substring(prefix.length); //Changed as per suggestions
return butnum;
}
function deleteDish(field) {
var numbut = getbutNum();
//Delete the UL/LI
console.log("Num But" + numbut);
}
Asides from all else this gives me an error of 'unrecognized expression: data("field")'
Have you tried selecting your actual data attribute:
var num = element.attr("data-field").substring(prefix.length);
Or:
var num = element.data("field").substring(prefix.length);
EDIT
First add a class to your anchor element (I'm going under the assumption that you have more than one of these):
Delete Dish
Then:
$(".delete-dish").on("click", function (e) {
e.preventDefault();
var fieldData = $(this).data("field"),
num = fieldData.substring(fieldData.lastIndexOf("_") + 1);
console.log("Num But" + num);
});
Here is a fiddle to demonstrate
Using the attribute name that contains your input should work:
function getNum(element, attrPrefix) {
//set prefix, get number
var prefix = attrPrefix;
var num = element.attr("data-field").substring((prefix.length));
return num;
}
http://jsfiddle.net/zf3hmo4q/
Considering you want to parse attributes with "data-*" name:
function getNum(element, dataName, dataPrefix) {
var num = element.data(dataName).replace(dataPrefix, "");
return num;
}
console.log(getNum($(".btn"), "field", "bc_inventorybundle_menu_product_"));
Maybe something like this?
var getNumberFromAttribute = function(id, field) {
var field = $(id).data(field);
var parts = field.split("_");
return parts[parts.length - 1]
}
Here's a jsfiddle http://jsfiddle.net/o6go79cL/
UPDATE
You could just pass in the element. The only purpose of the id was to select the object. So you could also just do:
var getNumberFromAttribute = function(elm, field) {
var field = $(elm).data(field);
var parts = field.split("_");
return parts[parts.length - 1]
}
number = getNumberFromAttribute(anchorTag, "field");

JavaScript: Why isn't my closure working?

The following code is only assigning the value of the last .enter_form input to the last MYAPP.list[0].responses[MYAPP.score.round].form[key] (where key is the only thing that varies). I think it's because only the last value of the key is being passed to addEntry(), but I can't figure out how to get around that.
$('.enter_form input').each(function() {
var key = $(this).attr('id');
var val = $(this).val();
userDict[key] = val;
MYAPP.list[0].responses[MYAPP.score.round].form = [];
function addEntry() {
return function(k) {
MYAPP.list[0].responses[MYAPP.score.round].form[k] = {'entry': userDict[k]};
}(key);
}
addEntry();
}
Your addEntry function is redundant since each iteration is already run inside it´s own scope so key and val are preserved properly (hope that explanation makes sense). Also the array you where inserting into was overwritten each iteration as well, so at the end of the .each() you end up with an array with only 1 value. It should also be an object rather then an array, even if the id's are numerical.
// you where overwriting this each iteration
MYAPP.list[0].responses[MYAPP.score.round].form = {};
$('.enter_form input').each(function() {
var el= $(this); // cache instead of creating a new jQuery object each time
var key = el.attr('id');
var val = el.val();
userDict[key] = val;
MYAPP.list[0].responses[MYAPP.score.round].form[key] = {'entry': userDict[key]};
}); // ); was also missing
Should work.
It's a bit hard to work out what it's meant to do, but I think this is probably it:
MYAPP.list[0].responses[MYAPP.score.round].form = [];
$('.enter_form input').each(function() {
var $this = $(this),
key = this.id,
val = $this.val();
userDict[key] = val;
MYAPP.list[0].responses[MYAPP.score.round].form[key] = {
'entry': val
};
});
That's based on your saying that "...key is the only thing that varies" (presumably $(this).val() also varies, but I took your point). It will add entries to MYAPP.list[0].responses[MYAPP.score.round].form for each of the form's input ids, as well as adding them to the userDict map.
As a side note, if the id values on the input elements aren't purely numeric, then I suspect you want to start with a blank object:
MYAPP.list[0].responses[MYAPP.score.round].form = {};
// ^^-- change is here
...rather than an empty array:
MYAPP.list[0].responses[MYAPP.score.round].form = [];
...although since arrays are objects, it works even if you're adding non-numeric properties.
Off-topic: No need for $(this).attr('id'). Just use this.id.

Categories

Resources