jQuery: How do I target the first duplicate list item? - javascript

I have a listing of events. I'd like to remove any events that have duplicate names and dates, but they can have different locations. I can successfully drop duplicates using a variation of code from this thread - JQuery: Remove duplicate elements? However I'd like to hide the location info for the first duplicate item and I can't seem to target that. Here is my code so far...
$('li').each(function() {
var name = $(this).find('.name').text();
var dates = $(this).find('.dates').text();
var nameAndDates = name + dates;
if (seen[nameAndDates]) {
$(this).remove();
}
else {
seen[nameAndDates] = true;
}
});
Somewhere in there I need to target the first duplicate item and add something along the lines of...
$(this).find('.location').hide();
Any ideas?

count them and remove only if first duplicate
var seen = {};
$('li').each(function() {
var name = $(this).find('.name').text();
var dates = $(this).find('.dates').text();
var nameAndDates = name + dates;
if (nameAndDates in seen && seen[nameAndDates] === 1) {
$(this).find('.location').hide();
}
seen[nameAndDates] = nameAndDates in seen ? seen[nameAndDates] + 1 : 1;
});

Related

Target Array Value Directly After Selected One

I'm really sorry for I am aware similar questions have already been asked, but whenever I try to do it myself, none seem to apply or work. Basically when a user clicks on one of the elements, I am trying to get the following variables:
the id of the selected element
an array with all of the values prior to selected + selected one
an array with all of the values post-selected (selected not included)
the id of the element directly following the selected one
Thanks to your help in different posts, I have so far managed to complete the first two ones (in italic), but am unable to achieve the other two.
Would anyone know how to do so please? Thank you all in advance for your help!!
jQuery:
var days = ['#monday','#tuesday','#wednesday','#thursday','#friday','#saturday','#sunday'];
$('.days').on('click', function() {
var day = '#'+this.id;
var index = days.indexOf(day)+1;
var prev = days.slice(0, index);
var next = days.slice(index);
var above = days[index];
});
Should look more like this (though I really don't understand your code logic):
var dayIds = ['monday','tuesday','wednesday','thursday','friday','saturday','sunday'];
$('.days').on('click', function() {
//get selected element id
var dayId = this.id;
//find selected position in array and delete all values after
var dayPos = dayIds.indexOf(dayId);
var daysBelow = dayIds.slice(0, dayPos + 1;
//find position of item directly after selected and get value
var dayAfterPos = dayIds.indexOf(dayId) + 1;
var dayAfter = dayIds[dayAfterPos]; //(not working)
//only keep values following selected one
...
console.log(floorsBelow, floorId);
});
This is how you need to slicing the array. I'm not sure all the requirements you have so I have just taken out a snippet to demonstrate how you can get your values.
var dayIds = new Array('#monday','#tuesday','#wednesday','#thursday','#friday','#saturday','#sunday');
const current = '#thursday';
const currentIndex = dayIds.indexOf(current)+1;
const prev = dayIds.slice(0, currentIndex);
const next = dayIds.slice(currentIndex);
console.log(current); //#thursday
console.log(prev); //[ '#monday', '#tuesday', '#wednesday', '#thursday' ]
console.log(next); // [ '#friday', '#saturday', '#sunday' ]
EDIT:
Added newVar to contain next value
var dayIds = new Array('#monday','#tuesday','#wednesday','#thursday','#friday','#saturday','#sunday');
const current = '#thursday';
const currentIndex = dayIds.indexOf(current)+1;
const prev = dayIds.slice(0, currentIndex);
const next = dayIds.slice(currentIndex);
const newVar = dayIds[currentIndex];
console.log(current); //#thursday
console.log(prev); //[ '#monday', '#tuesday', '#wednesday', '#thursday' ]
console.log(next); // [ '#friday', '#saturday', '#sunday' ]
console.log(newVar); // #friday

if statement inside jQuery selector

I'm getting those 2 vars from the DOM:
var get_category = $('#category').find('.current').attr('rel');
var get_subcategory = $('#subcategory').find('.current').attr('rel');
and I want here to find the classes in my DOM and show it
$('.filter-result').find('.'+get_category, '.'+get_subcategory ).show();
But I need to write it inside the .find() only if the variables are exist
I hope it answers your question:
var get_category = $('#category').find('.current').attr('rel');
var get_subcategory = $('#subcategory').find('.current').attr('rel');
var classes = [];
if (get_category) {
classes.push('.' + get_category);
}
if (get_subcategory) {
classes.push('.' + get_subcategory);
}
//if get_category or get_subcategory were found
if (classes.length) {
$('.filter-result').find(classes.join('')).show();
}
I do like Gabriels answer because it is very simple another option that works well and is extensible all you would have to do add another selector is add it to the selectors array. It is a little bit more advanced using javascripts filter and map array methods though.
var get_category = $('#category').find('.current').attr('rel');
var get_subcategory = $('#subcategory').find('.current').attr('rel');
var selectors = [get_category, get_subcategory];
var query = selectors.filter(function(elem) {
if (elem) { return elem };
}).map(function(elem){
return '.' + elem;
}).join(', ')
$('.filter-result').find(query).show();

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;
}

search for element within element by tagname

After being stuck for a few hours on this problem, i think it is time for call for help on this.
Situation
I have a XML file which i need to filter and group. I've managed to filter it with the :Contains part. I've also determined the nodes on which i need to group (the getGroups function gives those back to me). Now i want to create a new XML with the filtered values and grouped by the returned keys.
Code
var XMLElement = document.createElement("DataElementsCalc");
jQuery(xml).find("DataElements " + topNodes + filter).each( function() {
var dataSetTemp = this.parentNode;
if(calculation1 != "")
{
var groupKeys = getGroups(dataSetTemp,calculation1);
var tempXML = XMLElement;
jQuery(groupKeys).each(function (key,value) {
var tempValue = 'a' + value.toLowerCase().replace(/\W/g, '');
if(tempXML.getElementsByTagName(tempValue).length > 0)
{
tempXML = tempXML.getElementsByTagName(tempValue);
}
else
{
var Node = document.createElement(tempValue);
tempXML.appendChild(Node);
tempXML = Node;
}
});
var Node = document.createElement("InfoSet");
var x = dataSetTemp.childNodes;
for (i=0; i < x.length; i++)
{
if(x[i].nodeType == 1)
{
var tempElement = document.createElement(x[i].nodeName);
tempElement.innerHTML = x[i].childNodes[0].nodeValue;
Node.appendChild(tempElement);
}
}
tempXML.appendChild(Node);
}
});
Explanation
As said in the situation, i already filtered the XML and have the groupNames from the getGroups function. There are a few other things i need to explain for this code:
tempValue is being build as a a + value.toLowerCase().replace(/\W/g, '');. This is being done because i possible get dates into the groupKeys. This way the node name is getting a working name (i received errors on other ways).
I want to create a new XML which is leveled by the groups. If a group already exists, i want to create a new element in it, not get a new group with the same name. (my problem at the moment).
Problem
As mentioned above, the groups aren't checked properly. Firstly: tempXML.getElementsByTagName(tempValue).length returns the error that the function tempXML.getElementsByTagName does not exists. Secondly: If i change this to document.getElemetsByTagName I get a lot of the same nodes in my XML file.
Effect
The grouping effect doesn't work as it should. I get OR an error, OR a lot of the same nodes in my DataElementsCalc.
Questions
How can i solve this? How do create nodes beneath specific nodes (for if there is a group A beneath group 1 as well as beneath group 2)?
Tried
Change tempXML to document on different places (all getElementsByTagName, at the same time or not). Looked for another way to create a XML which is easier to handle (haven't found one, yet)
As mentioned by myself in the comments of the question:
I also don't see anything in the source code for this (maybe this is the reason why it doesn't work??)
I tried to place the XMLElement into an existing element on my webpage (like this:
var XMLElement = document.createElement("DataElementsCalc");
jQuery('.basicData').append(XMLElement);
in which basicData is a class of an existing element).
Now i do get a list of all elements ordered on the groups i wanted.
Final version
var XMLElement = jQuery("<DataElementsCalc/>");
jQuery('.basicData').append(XMLElement);
jQuery(xml).find("DataElements " + topNodes + filter).each( function()
{
aantalElementsTotal++;
var dataSetTemp = this.parentNode;
if(calculation1 != "")
{
var groupKeys = getGroups(dataSetTemp,calculation1);
var tempXML = XMLElement;
var groupId = '';
jQuery(groupKeys).each(function (key,value) {
var tempValue = 'a' + value.toLowerCase().replace(/\W/g, '');
groupId += 'a' + value.toLowerCase().replace(/\W/g, '');
if(jQuery("#" + groupId).length > 0)
{
tempXML = jQuery("#" + groupId);
}
else
{
var Node = jQuery("<"+tempValue+"/>");
jQuery(Node).attr('id', groupId);
jQuery(tempXML).append(Node);
tempXML = Node;
}
});
var Node = jQuery("<InfoSet/>");
var x = dataSetTemp.childNodes;
for (i=0; i < x.length; i++)
{
if(x[i].nodeType == 1)
{
var tempElement = jQuery("<"+x[i].nodeName+"/>");
jQuery(tempElement).text(x[i].childNodes[0].nodeValue);
jQuery(Node).append(tempElement);
}
}
jQuery(tempXML).append(Node);
}
});

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");

Categories

Resources