storing html-nodes as key in array - javascript

I want to store elements as the keys in my array and object as values,for example -
var arr = [];
arr[ document.getElementById('something') ] = { data: 'something' , fn : function(){ } };
But the problem is: If I will add another element with the key of : document.getElementById('otherthing').
And later will try to get the value of : arr[ document.getElementById('something') ].data , I will get the value of arr[ document.getElementById('otherthing') ].For Example :
var arr = [];
arr[ document.getElementById('something') ] = { data: 'something' , fn : function(){ } };
arr[ document.getElementById('otherthing') ] = { data: 'otherthing' , fn : function(){ alert('k'); } };
alert( arr[ document.getElementById('otherthing') ].data ); // alerts "otherthing"
alert( arr[ document.getElementById('something') ].data ); // alerts "otherthing" but suppose to alert "something"
How I can fix this problem,I can`t save by id,because I want to support other nodes with no-id
Thanks,Yosy.
EDIT:My answer to this,If you have better answer please write it :) (Inspired by casablanca`s answer)
array for id: key-integer the node id, value the node it self
and the array with data and fn with key of my id,It will look like this :
var idArray = [],nodeArray = [];
idArray[0] = document.getElementById('hello_ducks');
nodeArray[0] = { data: 'hello ducks!!' , fn : function(){ alert('k'); } };
idArray[1] = document.getElementById('hello');
nodeArray[1] = { data: 'hello' , fn : function(){ } };
var testNode = document.getElementById('hello_ducks'), foundId = -1 /*found id*/;
// Do we have testNode in our array?
for(var i = 0 ; i < idArray.length; i++ ){
if( idArray[i] === testNode ){
foundId = i;
}
}
// Do we found our element?
if(foundId >= 0) {
alert( nodeArray[foundId].data ); // "hello ducks!!"
}

I can`t save by id,because I want to support other nodes with no-id
You should keep in mind that in order to get back a node from the array, you have to somehow identify it. If a node doesn't have any such unique identifier, then how do you retrieve it later from the array, or even store it in the first place?
In lower-level languages like C++, every object implicitly has a unique address, but there is no such thing you can use in JavaScript, so you need to manually provide some way of identifying an object, and the DOM ID is the most convenient way of doing this.
If some of your nodes don't initially have an ID, the best way to proceed is to simply assign your own unique ID.
Update: The solution you posted will work, but it's not very efficient because you need to search the entire array every time you need to find a node. Why not simply assign your own ID to those elements which don't have one? For example:
var nextID = 0;
function getID(elem) {
if (elem.hasAttribute('id') == false || elem.id == '')
elem.id = 'dummy-' + (++nodeID);
return elem.id;
}
This way you can always use the ID as a key:
var nodeArray = [];
var e = document.getElementById('hello');
nodeArray[getID(e)] = { ... };
var e = /* element obtained from somewhere, doesn't have an ID */
nodeArray[getID(e)] = { ... }; // still works

I would suggest that instead of using an array for this purpose, you take advantage of the DOM-elements being objects:
document.getElementById('something').info = {data: 'something', fn: function () { } };
but you may end up with some issues with memory leaks in certain older browsers (read: ie6). That is fixed if you use jQuery's data storage instead:
$("#something").data("info", {data: 'something', fn: function () { } });

Related

How to loop through all the array of objects?

I have an Array of Object to be used to draw a HTML Table:
Array(5)
0: Object
id: 4
name: Sand Jane
address: Green Sand Street
...
...
...
1: Object
2: Object
...
...
...
I am able to do a single name column defined search with
const temp = this.temp.filter(function (d) {
return d.name.toLowerCase().indexOf(val) !== -1 || !val;
});
temp will contain the filtered data of this.temp
Now I just can't figure out how to loop through all object keys (id,name,address...) so that I can do a global column search in the Table.
UPDATE: I have tried this,
const temp = [];
this.temp.forEach(element => {
for (let key in element) {
if (element.hasOwnProperty(key)) {
let v = element[key];
if (v.toString().toLowerCase().indexOf(val) !== -1 || !val) {
temp.push(element);
}
}
}
});
It works but with performance issues.
RESOLVED: Putting a break after temp.push fixed this issue. Thank you all for the guidelines and references.
temp.forEach(item=>{
for(let key in item) {
//item[key] gives you access to each value
}
})
You can iterate through for loops or even you can usefor loop inside a for loop. Either way it's self explanatory.
var dictionary = {
"employee1":[
{"id":"0","name":"Google"},
{"id":"1","name":"eBay"}
],
"employee2": [
{"id":"2","name":"Yahoo"},
{"id":"3","name":"Facebook"}
]
};
var employee1 = dictionary.employee1;
var employee2 = dictionary.employee2;
for ( var i in employee1) {
var id = employee1[i].id;
var name = employee1[i].name;
console.log(id);
console.log(name);
}
for ( var i in employee2) {
var id = employee2[i].id;
var name = employee2[i].name;
console.log(id);
console.log(name);
}

FInd object in array by value and update entire object

I have a list of objects and sometimes I receive an update from the API for one of those objects and what I need to do is to find the object with the id of the one to update and update the entire object...
I was trying to avoid a for loop because the list could be very very long.
So what I was trying to use is $.grep but it doesn't seem to work as expected.
Here is what I tried so far:
// item is the response data from the API
var item = res.item;
var index = $.grep(arrayOfItems, function (e, i) {
if (e.id === item.id) {
return i;
}
});
arrayOfItems[index] = item;
the item is not updated unfortunately...
If it's speed you're after, especially with a long list, you may consider indexing your list by id when you first retrieve it, making updates later quicker than having to loop the entire array to find an index.
To demonstrate, assume you have retrieved an array of objects
var data = [
{id:1,data:'hello'},
{id:2,data:'world'},
{id:3,data:'foo'},
{id:4,data:'bar'}];
now create an object which represents your data where the property is the Id (object properties cannot start with a number, so if id is numeric, prefix it) and the value is the index back into the original array. So, the above data would be transformed to
var dataIndex = {
id1:0,
id2:1,
id3:2,
id4:3
};
This can be done trivially with a function
function indexDataById(data)
{
var index = {};
$.each(data, function(e,i){
index['id' + e.id] = i;
});
return index;
}
var dataIndex = indexDataById(data);
Now, when it comes to your update, you can find the index instantly using the id
var updateId = 2;
var elementIdx = dataIndex ['id' + updateId];
data[elementIdx] = myNewData;
The one complication is that you need to go back and update the index if the id of the new data has changed:
var updateId = 2;
var elementIdx = dataIndex [`id` + updateId];
data[elementIdx] = myNewData;
delete dataIndex[elementIdx]
dataIndex['id' + myNewData.id] = elementIdx;
This should be easy enough to handle atomically with your update.
$.map and $.grep return both an array so you will never get the index.
Inside $.map or $.grep function you need to return true or false based
on your filter logic. They re not useful in your case.
if your structure is not ordered you can only loop trough it and stop the loop when you find your element... like that:
var item = res.item;
var index = "";
$.each(arrayOfItems, function(i,v){
if(item.id == v.id){
index = i;
return true;
}
});
arrayOfItems[index] = item;
if you wanna order your structure before loop use this:
arrayOfItems.sort(function(a, b) {
return a.id > b.id;
});
i ve made a fiddle with an example https://jsfiddle.net/L08rk0u3/
try this way using $.grep
var arrList = [
{name :11,id :11},{name :12,id :12},{name :111,id :111},
{name :13,id :13},{name :15,id :15},{name :11,id :11},
{name :41,id :41},{name :31,id :31},{name :81,id :81},
{name :91,id :91},{name :13,id :13},{name :17,id :17},
{name :1111,id :1111}
]
console.log(arrList);
var respItem ={name :1111000,id:1111};
var intSearchedIndex;
$.grep(arrList,function(oneItem,index){
if(respItem.id==oneItem.id){
return intSearchedIndex = index;
}
})
arrList[intSearchedIndex] =respItem;
console.log(intSearchedIndex,arrList);
Try with map method like this.
Code snippets:
// item is the response data from the API
var item = res.item;
var index = $.map(arrayOfItems, function (e, i) {
if (e.id === item.id) {
return i;
}
});
if(index.length)
arrayOfItems[index[0]] = item;
Update:
arrayOfItems[index] = item;
This will work if index array has an single element. See fiddle
But,
arrayOfItems[index[0]] = item;
This is the appropriate way since it is an array.

parsing and iterating over a large dataset - why are these results not lining up? (javascript)

I have a question about my JavaScript (vanilla JS, no library answers please).
setup
I have two .txt files, names and data.
names contains a list of 100 last names, one name per line, ie:
Nader
Sanford
Kovacek
Lynch
etc...
data contains a much longer list, of first names, last names, a comma and a value, ie:
Kailee Huel,2
Arianna Runolfsson,4
Marshall Kuhn,3
Cristina Huel,4
Garnett Medhurst,3
etc...
task
Iterate through data.txt using the last names in names.txt. If any of the last names from names.txt have a match in data.txt, increment a counter with the value on that line (after the comma). Some last names are repeated in both names.txt and data.txt.
problem I am encountering:
I have been given that my sample data set must return a value of 443, however the result I am getting is 500. this tells me I am making a blunder with my processing somewhere in my code below.
my code:
// doc rdy
document.onreadystatechange = function(){
if( document.readyState == "complete" ){
init();
}
}
// setup
function init(){
var base = "data/sample/",
namesFile = base+"names.txt",
dataFile = base+"data.txt";
loadFile(namesFile, function(e){
names = e;
loadFile(dataFile, function(ev){
processData(ev);
});
});
}
function processData(e){
var counter = 0;
// for each last name,
for( var i=0; i<names.length; i++ ){
// if "data object" contains "this name", add its val to counter
if( e.hasOwnProperty(names[i]) ){
// console.log( 'match: ' + names[i] );
counter += e[names[i]];
}
}
// The Final Output:
console.log(counter);
}
// ajax loading
function loadFile( filename, callback ){
var client = new XMLHttpRequest();
var result;
client.open('GET', filename);
client.onreadystatechange = function() {
if( client.readyState==4 && client.status==200 ){
result = client.responseText.split("\n");
// custom handling for the data set..
if( result[0].search(',') !== -1 ){
result = arrCSVtoObj(result);
}
callback(result);
}
}
client.send();
}
// converts an array of CSV to a key:value object
function arrCSVtoObj(csvArr) {
var obj = {};
// for each line in the array,
for( var i=0; i<csvArr.length; i++ ){
// split into key:value on comma
var split = csvArr[i].split(",");
var key = getLastName(split[0]);
var val = parseInt(split[1]);
// assign the new property to the object
if( ! obj.hasOwnProperty(key) ){
obj[key] = val;
}else{
obj[key] += val;
}
}
return obj;
}
function getLastName(e){
var nameArr = e.split(" ");
return nameArr[1];
}
question:
What in the above code could be causing my result to return higher than anticipated?
========
EDIT:
Here is the sample data I am using. It is returning a value of "500" when it should be returning a value of "443".
names.txt
data.txt
If a name appears in names.txt multiple times, you're adding it to the counter each time. If you don't want to do that, you need to keep track of which names you've already processed, and skip them when they occur again:
var namesProcessed = {};
function processData(e){
var counter = 0;
// for each last name,
for( var i=0; i<names.length; i++ ){
// if "data object" contains "this name", add its val to counter
if( e.hasOwnProperty(names[i]) && !namesProcessed.hasOwnProperty(names[i]) ){
// console.log( 'match: ' + names[i] );
counter += e[names[i]];
namesProcessed[names[i]] = true;
}
}
// The Final Output:
console.log(counter);
}
If you're using a library like jQuery or Underscore.js, they provide functions to get the unique elements of a list: $.unique() and _.uniq(). Use this on the names array before iterating.

javascript only remove first match in array in loop

I have an array of Exclusions like below:
Exclusions: [ID:"233242", Loc:"West", ID:"322234" , Loc:"South"]
I also Object nested with an array of objects that could look something like
Schools : [ O: [ ID:"233242" ] , 1:[ ID:"233242"] , 2: [ID :"954944"] ]
I need to delete from the schools object any matching array indices with the same ID but only for the first match. That means element 0 should be removed, but element 1 should still be there. What's the best way to fix my loop:
$.each(Exclusions, function (index, value) {
var loc = value.Loc;
var ID = value.ID;
Object.keys(Schools.District.Pack[loc]).forEach(function (key) {
//i need to scan through the entire object
if (Schools.District.Pack[loc].ID === ID) {
//remove the first match now stop looking
Schools.District.Pack[loc].splice(key, 1);
//break ; incorrect
}
});
});
I'd say having another lookup array for removed IDs, and you'll need something like this
var Exclusions = [{ID:"233242", Loc:"West"}, {ID:"322234" , Loc:"South"}];
var Schools = [{ ID:"233242" } ,{ ID:"233242"} , {ID :"954944"} ];
var removedKeys = [];
$.each(Exclusions, function (index, value) {
var loc = value.Loc;
var ID = value.ID;
Object.keys(Schools).forEach(function (key) {
//i need to scan through the entire object
if ((Schools[key].ID === ID) && (removedKeys.indexOf(ID) == -1)) {
removedKeys.push(ID);
//remove the first match now stop looking
delete Schools[key];
}
});
});
console.log(removedKeys);
console.log(Schools);
Hope this would help
fiddle

Javascript : Select Element in URL with multiple instance of the same element

i need to retrieve a value from an URL in JS, my problem is in this url, the element is repeated at least twice with different value. What i need is the last one.
Example :
http://randomsite.com/index.php?element1=random1&element2=random2&element1=random3
and what i want is "random3" and NOT "random1"
I've tried url.match(/.(\&|\?)element1=(.?)\&/)[2];
But it always gives me the first one :(
I don't have the possibility to change how the url is written as this is for a browser extension.
var ws = "http://randomsite.com/index.php?element1=random1&element2=random2&element1=random3",
input = ws.split("?")[1].split("&"),
dataset = {},
val_to_find = "element1";
for ( var item in input){
var d = input[item].split("=");
if (!dataset[d[0]]){ dataset[d[0]] = new Array(); dataset[d[0]].push(d[1]); }
else{
dataset[d[0]].push(d[1]);
}
}
console.log("item: ", dataset[val_to_find][dataset[val_to_find].length -1]);
return dataset[val_to_find][dataset[val_to_find].length -1];
http://jsfiddle.net/wMuHW/
Take the minimum value (other than -1) from urlString.lastIndexOf("&element1=") and urlString.lastIndexOf("?element1="), then use urlString.substring.
Or alternately, split the string up:
var parts = urlString.split(/[?&]/);
...which will give you:
[
"http://randomsite.com/index.php",
"element1=random1",
"element2=random2",
"element1=random3"
]
...then start looping from the end of the array finding the first entry that starts with element= and grabbing the bit after the = (again with substring).
You could;
for (var result, match, re = /[&?]element1=(.+?)(\&|$)/g; match = re.exec(url);) {
result = match[1];
}
alert(result);
Id try keeping a nested array of duplicate elements
function parseQueryString() {
var elements = {},
query = window.location.search.substring(1),
vars = query.split('&');
for (var i = 0; i < vars.length; i++) {
var pair = vars[i].split('='),
key = decodeURIComponent(pair[0]),
value = decodeURIComponent(pair[1]);
if (elements.hasOwnProperty(key)) {
elements[key].push(value);
}
else {
elements[key] = [value];
}
}
}
Used on: www.example.com?element1=hello&element2=test&element1=more
Would give you the object:
{
element1: [
"hello",
"more"
],
element2:[
"test"
]
}

Categories

Resources