Data-structure to store "last 10" data sets? - javascript

I'm currently working with an object Literal to store temporary information to send to clients, it's like a history container for the last 10 sets of data.
So the issue that I', having is figuring out the most efficient way to splice on object as well as push an object in at the start, so basically i have an object, this object has 0 data inside of it.
I then insert values, but what I need to do is when the object reaches 10 keys, I need to pop the last element of the end of object literal, push all keys up and then insert one value at the start.
Take this example object
var initializeData = {
a : {},
b : {},
c : {},
d : {},
e : {},
f : {},
g : {},
h : {},
i : {},
j : {}
}
When I insert an element I need j to be removed, i to become the last element, and a to become b.
So that the new element becomes a.
Can anyone help me solve this issue, I am using node.js but native JavaScript is fine obviously.
Working with arrays after advice from replies, this is basically what I am thinking your telling me would be the best solution:
function HangoutStack(n)
{
this._array = new Array(n);
this.max = n;
}
HangoutStack.prototype.push = function(hangout)
{
if(this._array.unshift(hangout) > this.max)
{
this._array.pop();
}
}
HangoutStack.prototype.getAllItems = function()
{
return this._array;
}

Sounds like it would be a lot easier to use an array. Then you could use unshift to insert from the beginning and pop to remove from the end.
Edit: Example:
var items = []
function addItem(item) {
items.unshift(item)
if (items.length > 10) {
items.pop()
}
}
Alternatively, you could use push/shift instead of unshift/pop. Depends on which end of the array you prefer the new items to sit at.

What you need is called a Circular Buffer.
Have a look at this implementation in javascript

Yeah, use an Array.
var arr = [];
// adding an item
if (arr.unshift(item) > 10) {
arr.pop();
}
If you need the 'name' of the item, like "a" or "b" in your object example, just wrap each item in another object that contains the name and the object.
Objects in js are like dictionaries -- they have no inherent order to the items. It's just a collection of things. You can try to make up an order (like a through z in your example), but then you have to manage that order yourself when things change. It's just much easier to use an array.

Related

Add a key to an object with keys of an existing array with objects

I've got an array of objects array = [object1, object2, ...], each of them has some keys object1 = { key1: 'value1', ... }. I want to add a key this way:
$rootScope.array[i].newKey = 'someValue'
But angular tells me that $rootScope.array[i] is undefined.
What I've noticed from console is that the objects get the new key but the console still says the same.
You should use less than and not less or equal than comparator.
$scope.init = function () {
for (i = 0; i < /* not <= */ $rootScope.meatTypes.length; i++) {
console.log("I am showing the meatypes:");
console.log($rootScope.meatTypes);
$rootScope.meatTypes[i].counter = '0';
counterOperations.setCounters(i, 0);
}
$rootScope.total = 0;
counterOperations.setTopCounter(0);
};
because when i equals $rootScope.meatTypes.length then $rootScope.meatTypes[i] is undefined.
You are trying to access a member of the array that does not exist.
You need to create a new object and push it onto the array:
$rootScope.array.push({'key1': 'someValue'});
You did not mention lodash, but when I see someone encounter an issue like this, I want to offer the recommendation of using lodash (or underscore.js).
With lodash, you would do something like so, using _.set, which defensively protects against your described issue by automatically adding the necessary elements in the path:
_.set($rootScope, ['array', i, 'newKey'], 'someValue');
This library, properly utilized, solves many issues that you can have with setting and getting variables, ase well as other super useful tools. It has been a major life-saver (and time-saver) for us on our projects.
Like this you can add
$rootScope.array[i] = {}; // first we should create an object on that array location
$rootScope.array[i]['newKey'] = 'someValue'; // then only we can add values
EDIT:
$scope.init = function () {
for (i = 0; i <= $rootScope.meatTypes.length; i++) {
console.log("I am showing the meatypes:");
console.log($rootScope.meatTypes);
**// This is needed**
$rootScope.meatTypes[i]={};// here we should tell that metaType[newItem] is an object other wise it treat it as undefined
$rootScope.meatTypes[i].counter = '0';
counterOperations.setCounters(i, 0);
}
$rootScope.total = 0;
counterOperations.setTopCounter(0);
};

Check if item from one object exists in another (using underscore)

I am trying to compare 2 objets using underscore, specifically I am trying to compare the key/values of "id" (because other things inside will change). I basically want to just check if object A has an item that object B does not have, remove it from object A. Here is my attempt at it :
for(var c=0;c<$scope.types.length;c++){
var real = _.some($scope.storeTempName, function(it) {
return it.id == $scope.types[c].typeId;
});
if(real){
}else{
$scope.types.splice(c,1);
}
}
Where $scope.storeTempName is object B and $scope.types is object A. So if $scope.types has something $scope.storeTempName does not, remove it (tracking by id and typyId for types).
This first attempt I have works, BUT it only will remove the first one. My guess is it's becasue I'm looping from 0 ++ and the index's are changing when i remove the first one so splice is targetting a false item. I am not sure though, and could use some help. Thank you for reading!
Just use _.filter.
$scope.types = _.filter($scope.types, function (type) {
return _.some($scope.storeTempName, function (it) { return it.id == type.typeId })
})

how do I reference a specific object in array that has changed with angular $watch

I am attempting to calculate the new total whenever price or qty change. I don't understand how to get a reference to the specific object that has changed.
$scope.parts = [{description: null, price: 0, qty: 0, subtotal: null}]
$scope.partsTotal = 0
$scope.$watch('parts', =>
$scope.partsTotal += $scope.parts.price * $scope.parts.qty
$scope.parts.subtotal = $scope.parts.price * $scope.parts.qty
, true)
Create a new watch on each object in the array, and then use watch with the first parameter being a function returning the object (I'm using underscorejs to iterate over the array):
UPDATE: you might lost the reference using _.each(), so if that doesn't work try a for loop...
for(var i=0; i<$scope.parts.length; i++) { var part = $scope.parts[i]; ... }
_.each($scope.parts, function(part) {
$scope.$watch(
function() { return part; },
function(newVal, oldVal) {
if(newVal.price !== oldVal.price || newVal.qty !== oldVal.qty) {
//run the update
}
}
);
});
You have an object literal inside of an array literal.
You would have to access the "subtotal" property in your example like this:
$scope.parts[0].subtotal
$watch(es) get the new values and the old values passed in when they fire. I would try to reproduce with your example but I can't bring myself to write it with Coffee. You can grab the values from the arguments going in, or you can access the object directly like you are there (which might be easier considering you're using a deep watch).

Two Dimensional Array in Javascript Object

I want to create an Object that contains one or more two dimensional arrays in Javascript.
I tried it the following way (in this example I only try to add one two dimensional array):
var XSIZE = 8;
var YSIZE = 8;
var obj = {
field : new Array(XSIZE),
field[0] : new Array(YSIZE),
foo : 1,
bar : 100
}
Info:
- This gives me a strange error "missing : after property id" which does not seem to make much sense
- Unfortunately I didn't find examples showing how to do this so far by using google
- If I don't add field[0] ... for creating the 2nd array it works.
- changing the XSIZE and YSIZE to numbers like new Array(8)... doesn't work.
I would really appreciate if somebody could show me how to do it or explain why I cannot do this at all and need to use some other method.
Thanks a lot!
The error "missing : after property id" is because JavaScript sees the field part of field[0] and expects a colon before the value of that field. Instead it gets an open bracket so it complains.
You can't hard code an object definition that has its dimensions set up at run time. You have to build the object at run time as well. Like this perhaps
var XSIZE = 8;
var YSIZE = 8;
var obj = {
field : new Array(),
foo : 1,
bar : 100
}
for (var i = 0; i < XSIZE; i++) {
obj.field.push(new Array(YSIZE));
}
In object literal notation, the property names must be exactly that: property names. Firstly, field[0] isn't a property name. Secondly, the properties don't exist until the after the object defined, so you can't access properties until then.
What you should do is either set the array after the object is created:
var obj = {...}
obj.field[0] = [...];
or nest the array literals:
var obj = {
field: [ [...],
...
],
...
}
You don't need to worry about setting the array size when creating the array, as it will grow when you add elements.
You can only declare properties on the object being constructed that way; not on objects in another "level".
You could use a for loop instead:
for(var i = 0; i < XSIZE; i++) {
obj.field[i] = new Array(YSIZE);
}
Note that the YSIZE is not necessary since an empty array works just fine as well ([]).
You could get the two dimensional array as your obj property, without resorting to external procedures and keep everything internal to the object. Create your empty 'field' array 1st.
var obj = {
field:[],
foo:1,
bar:100
};
Now, create an object's method to create a two dimensional array off your initial dimensionless array. You can determine the length and the number of dimensions of multi dimension array as you wish at run time:
var obj = {
field:[],
multifield:function(x,y){for (var count=0;count<x;count++) {this.field[count]=new Array(y);}},
foo:1,
bar:100
};
You can then call the obj.multifield method entering whatever dimensions you decide:
obj.multifield(10,5); //-->create a 10x5 array in this case...
console.log(obj.field.length); // 10
console.log(obj.field[0].length); // 5

How to implement such an associative array?

arr[key] = value;
where key is a jQuery object and value is an array.
Associative arrays don't really exist in JavaScript. However, you can achieve similar functionality using JavaScript objects:
// Create object
var myObject = {
key: value,
helloText: "Hello World!"
};
// Access object in some statement via:
myObject.helloText
// ...or:
myObject["helloText"]
To use an object as a key, you would have to do something like:
var a = {
helloText: "Hello World!"
};
var b = {};
b[a] = "Testing";
alert(b[a]); // Returns "Testing" (at least, in Safari 4.0.4)
Using an object as a key sounds a bit weird, though. Are you sure you need to do this?
Update:
You can't actually use an object as a key in JavaScript. The reason the above code appears to work is that, in the statement b[a] = "Testing";, JavaScript converts a to a string via a.toString(), which results in "[object Object]", and uses this string as the key. So our statement is actually b["[object Object]"] = "Testing"; and our alert statement is exactly the same as alert(b["[object Object]"]);.
Thanks to CMS for pointing this out in the comments!
Update 2:
Tim Down points out that his JavaScript library jshashtable allows you use an object as a key.
You can use jshashtable, which allows any JavaScript object as a key.
Just guessing here, but it seems you're trying to associate some (arbitrary) data with a jQuery object (possibly an element). In that case, why not use the data () method?
$('#el').data (value);
You can't use objects as keys, and assocative arrays are not what they seem in Javascript because all you're doing is setting a property on the array object, when you loop through by the .length it natively doesn't account for the manually defined properties you set.
I suggest storing the elements and arrays inside of object literals, all inside of an array. Eg:
var list = [
{
el:document.body,
arr:[1,2]
}
];
for ( var i = 0, l = list.length; i<l; ++i ) {
alert( list[i]['el'] )
alert( list[i]['arr'][0] )
}
// add elements to the array
list.push({
el:document.body.firstChild,
arr:[3,4]
})
As kprime mentioned in his answer though, it might be better to use .data() if you are using Javascript.
if ( !$(el).data('key') ) {
$(el).data('key', [2,3,4] );
}
I would suggest assigning a unique ID to each element you want to put in the associative container (object in JS) and use the ID as key:
var idCounter = 0;
var container = { };
function storeValue(element, value) {
if (!element.getAttribute('id')) {
element.setAttribute('id', makeID());
}
var id = element.getAttribute('id');
container[id] = value;
}
function makeID() {
return 'unique-id-' + idCounter++;
}
EDIT: This solution assumes that jQuery is not available. If it is, use data('key', value).
every javascript object is an associative array, this is a property build in the language, you do not need to anything special, just use it like that

Categories

Resources