i'm trying to sort my hash table pull down menu alphabetically... using this function:
function getSortedKeys(obj) {
var keys = [];
for(var key in obj) {
keys.push(obj[key]);
keys[keys.length-1]['key'] = key;
}
return keys.sort(function(a,b){
return a.name > b.name ? 1 : a.name < b.name ? -1 : 0;
});
}
This sorts the pull down menu...although it changes the original id# of my menu items which screws some things up on my site... is it possible to keep the original id# of each menu item and still sort?
sorry..here's the hash code:
var clientProjectsHash = {};
clientProjectsHash['1'] = {};
clientProjectsHash['1']['name'] = 'RONA';
clientProjectsHash['2'] = {};
clientProjectsHash['2']['name'] = 'CMS';
clientProjectsHash['3'] = {};
clientProjectsHash['3']['name'] = 'ALT';
and getSortedKeys is called by:
function getInitialClient() {
clientProjectsHash = getSortedKeys(clientProjectsHash);
for (clientKey in clientProjectsHash) {
if(clientKey > 0) {
return clientKey;
}
}
}
The problem is you are returning an array, and expecting it to be an object. These are different things in JavaScript. Nothing is "changing"; your "ids" (or "hashes") are not being modified.
Your clientProjectsHash starts as an object! Objects are unordered and can have any string as a key. When you do getSortedKeys(clientProjectsHash); you are being returned an array! Arrays are ordered, and have numeric indexes (keys) that start at 0.
clientProjectsHash = getSortedKeys(clientProjectsHash);
for (clientKey in clientProjectsHash) {
}
This overwrites clientProjectsHash with an array. Then you for..in over it (you should not use for..in for arrays, by the way).
The array returned from getSortedKeys looks like this:
[
{
name: 'ALT',
key: 3
},
{
name: 'CMS',
key: 2
},
{
name: 'RONA',
key: 1
}
]
So, in your for..in, clientKey will be 0, 1, and 2. The indexes of the array. Your key values are not changing, you are just reading the wrong value.
Try this instead:
function getInitialClient() {
var clientKeys = getSortedKeys(clientProjectsHash);
for(var i = 0, len = clientKeys.length; i < len; i++){
var clientKey = clientKeys[i];
if(clientKey.key > 0){
return clientKey.key;
}
}
}
Related
hi all i am using javascript working with array i have a set of data to do add array values object here i attached my code help how to solve this
data
var data=[{one:1,two:2},{one:1,two:2},{one:1,two:2},{one:1,two:2}]
expected output
var sumdata=[{one:4,two:8}]
NOTE:one two column name is not static
var resObj = {};
for (var i = 0; i < data.length; i++) {
for (var item in data[i]) {
if (!resObj.hasOwnProperty(item)) {
resObj[item] = data[i][item];
} else {
resObj[item] += data[i][item];
}
}
}
var sumdata = [resObj];
You could use an object as result and iterate the keys of the object and sum the values.
var data = [{ one: 1, two: 2 }, { one: 1, two: 2 }, { one: 1, two: 2 }, { one: 1, two: 2 }],
result = data.reduce(function (r, o) { // iterate array
Object.keys(o).forEach(function (k) { // iterate the keys of the object
r[k] = (r[k] || 0) + o[k]; // check if a property exists or take zero
}); // and add the actual value
return r; // return the object
}, Object.create(null)); // start with an empty object without
// some prototypes
console.log(result);
I am aware that I could use .filter to achieve this, however I am not sure how to implement it.
I have objects as follows within an array
item {
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
}
however, some items in the array have a possible property of NaN.
I need to make sure that only items whose possible property is not NaN, are pushed into the array.
Here is an excerpt of my complete code:
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
if(g.attributes.possible !== g.attributes.possible){
return;
}
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
NaN is the only property in javascript that does not equal itself. Just loop over the properties and check them for this, or use the built in NaN() function within the loop as suggested elsewhere.
Update
Since you're only worried about the possible property, just check that one as part of the if statement using === self, or isNaN()
Just change your test line from
if(itemPath[i].attributes.id==id)
to use isNaN function on the properties you want to check
var attr = itemPath[i].attributes;
if (attr.id==id && !isNaN(attr.title) && !isNaN(attr.categoryid) && !isNaN(attr.possible))
You can use the isNaN() and test it before adding it...
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
if( isNaN(g.attributes.title) || isNaN(g.attributes.categoryid) || isNaN(g.attributes.possible) ){
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
}
});
}
}
}
You're code is a little confusing
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
items.push(
{
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
It doesn't look like you are using map right. Map works like list compresions in the sense that it iterates over a sequence to preform some kind of operation on each element and returns a new sequence
var arr = [1,2,3];
var complexMagic = arr.map( function(n) { return n + 10; } );
// complexMagic === [11,12,13]
FYI, this is how filter, works. Filter takes in a predicate function( aka, Boolean function) to build a new sequence. If the predicate returns true, then the element will be stored in the new sequence.
var arr = [1, 123, 42, 1001, 1100];
var oddNumbers = arr.filter( function(n) {
return 1 === (n & (-n) );
} );
// oddNumbers === [1, 123, 1001] );
// Bit hacks are fun ;P
It looks like you don't need items array or to even push new elements onto it.
function load(id){
itemPath = lev1.lev2.lev3;
items = [];
for (var i = 0; i<itemPath.length; i++) {
if(itemPath[i].attributes.id==id) {
return itemPath[i].attributes.grades.models.map(function(g) {
// You don't have to return anything.
// This might be an ok place for the NaN check.
return ({
title : g.attributes.title,
category : g.attributes.categoryid,
possible: g.attributes.possible
});
});
}
}
}
I'm lazy and didn't testing any of my code so reader beware. Also avoid the push method if possible. It can be a inefficient approach to append new elements onto an array.
I have an associative array like:
var arr = {};
arr['alz'] = '15a';
arr['aly'] = '16b';
arr['alx'] = '17a';
arr['alw'] = '09c';
I need to find the previous and next key of any selected element. Say, for key 'aly' it will be 'alz' and 'alx'. If possible, I want to access the array by index rather than the key.
Currently, I am doing this using a separate array containing keys, e.g.
var arrkeys = ['alz','aly','alx','alw'];
Ordering of the object's properties is undefined. You can use this structure...
[{ key: 'alz', value: '15a'},
{ key: 'aly', value: '16b'},
{ key: 'alx', value: '17a'}]
... though searching for the element with the given key (like 'give me the element which key is 'alz') is not as straight-forward as with simple object. That's why using it like you did - providing a separate array for ordering of the indexes - is another common approach. You can attach this array to that object, btw:
var arr={};
arr['alz']='15a';
arr['aly']='16b';
arr['alx']='17a';
arr['alw']='09c';
arr._keysOrder = ['alz', 'aly', 'alx', 'alw'];
This is an object, not an array, and it sounds like you don't really want those strings to be keys.
How about a nice array?
var ar = [
{ key: 'alz', value: '15a' },
{ key: 'aly', value: '16b' },
{ key: 'alx', value: '17a' },
{ key: 'alw', value: '09c' }
];
How about adding some syntactic sugar in the form of an OrderedObject object? Then you could do something like this:
myObj = new OrderedObject();
myObj.add('alz', '15a');
myObj.add('aly', '16b');
myObj.add('alx', '17a');
myObj.add('alw', '09c');
console.log(myObj.keyAt(2)); // 'alx'
console.log(myObj.valueAt(3)); // '09c'
console.log(myObj.indexOf('aly')); // 1
console.log(myObj.length()) // 4
console.log(myObj.nextKey('aly')); // 'alx'
The following code makes this work. See it in action in a jsFiddle.
function OrderedObject() {
var index = [];
this.add = function(key, value) {
if (!this.hasOwnProperty(key)) {
index.push(key);
}
this[key] = value;
};
this.remove = function(key) {
if (!this.hasOwnProperty(key)) { return; }
index.splice(index.indexOf(key), 1);
delete this[key];
}
this.indexOf = function(key) {
return index.indexOf(key);
}
this.keyAt = function(i) {
return index[i];
};
this.length = function() {
return index.length;
}
this.valueAt = function(i) {
return this[this.keyAt(i)];
}
this.previousKey = function(key) {
return this.keyAt(this.indexOf(key) - 1);
}
this.nextKey = function(key) {
return this.keyAt(this.indexOf(key) + 1);
}
}
I made some decisions that may not work for you. For example, I chose to use an Object as the prototype rather than an Array, so that you could preserve enumerating your object with for (key in myObj). But it didn't have to be that way. It could have been an Array, letting you use the property .length instead of the function .length() and then offering an each function that enumerates the keys, or perhaps an .object() function to return the inner object.
This could be a little awkward as you'd have to remember not to add items to the object yourself. That is, if you do myObj[key] = 'value'; then the index will not be updated. I also did not provide any methods for rearranging the order of things or inserting them at a particular position, or deleting by position. If you find my object idea useful, though, I'm sure you can figure out how to add such things.
With the newer versions of EcmaScript you can add true properties and make them non-enumerable. This would allow the new object to more seamlessly and smoothly act like the ideal OrderedObject I am imagining.
If you have to know the order of everything, and still use the keys and values, try this:
var arr = [
{ key: 'alz', value: '15a' },
{ key: 'aly', value: '16b' },
{ key: 'alx', value: '17a' },
{ key: 'alw', value: '09c' }
];
You can then access them sequentially as follows: arr[0].key and arr[0].value. Similarly, you can find siblings inside of the loop with the following:
for(var i = 0; i < arr.length; i++)
{
var previous_key = (i > 0) ? arr[(i - 1)].key : false;
var next_key = (i < (arr.length - 1)) ? arr[(i + 1)].key : false;
}
You may try this
function sortObject(obj, order)
{
var list=[], mapArr = [], sortedObj={};
for(var x in obj) if(obj.hasOwnProperty(x)) list.push(x);
for (var i=0, length = list.length; i < length; i++) {
mapArr.push({ index: i, value: list[i].toLowerCase() });
}
mapArr.sort(function(a, b) {
if(order && order.toLowerCase()==='desc')
return a.value < b.value ? 1 : -1;
else return a.value > b.value ? 1 : -1;
});
for(var i=0; i<mapArr.length;i++)
sortedObj[mapArr[i].value]=obj[mapArr[i].value];
return sortedObj;
}
// Call the function to sort the arr object
var sortedArr = sortObject(arr); // Ascending order A-Z
var sortedArr = sortObject(arr, 'desc'); // Descending order Z-A
DEMO.
Remember, this will return a new object and original object will remain unchanged.
So I have an array, which is populated with objects. The objects have two values, one containing a list item, and another containing a float (price). I'm trying to push the list into a DOM element, but the variable is globally undefined.
This code outputs the second array item to the console:
$('#coll-price-filter').change(function() {
var newList = [];
$('#coll-product-list li').each(function(idx, li) {
pprice = $(li).find('.coll-prod-price').html().trim().substring(1)
pprice = parseInt(pprice);
newList[idx] = {
value: $(li).wrap('<div></div>').parent().html(),
price: pprice
}
newList.sort(function(a, b){
a = a.price, b = b.price;
return a > b ? 1 : a < b ? -1 : 0;
});
});
console.log(newList[1].value);
});
However this does not
$('#coll-price-filter').change(function() {
var newList = [];
$('#coll-product-list li').each(function(idx, li) {
pprice = $(li).find('.coll-prod-price').html().trim().substring(1)
pprice = parseInt(pprice);
newList[idx] = {
value: $(li).wrap('<div></div>').parent().html(),
price: pprice
}
newList.sort(function(a, b){
a = a.price, b = b.price;
return a > b ? 1 : a < b ? -1 : 0;
});
});
i = newList.length;
while (i > 0) {
console.log(newList[i].listItem);
i--;
}
});
So it appears that the while loop is breaking the accessibility of the newList[] object. I've tried it a few ways, and it works if I'm inside the .each() iterator. But I need to access it outside.
Because Array indices are 0 based, this:
i = newList.length;
should be this:
i = newList.length - 1;
Otherwise the starting index is out of bounds, so newList[i] will be undefined, and accessing a property on undefined is a TypeError.
Once you correct that, you probably want to access a property defined on the object, like .value or .price, otherwise it will log undefined for each iteration.
Shouldn't this:
i = newList.length;
while (i > 0) {
console.log(newList[i].listItem);
i--;
}
Be this?
i = newList.length-1; // start on the last index, not length
while (i > 0) {
console.log(newList[i].value); // value, like in your top example
i--;
}
I have a data dictionary like this:
var data = {
'text1': 1,
'text2': 2,
'text3': 3,
...
'text20': 20
];
I need to pick a random selection of those keys and then shuffle it's values. In the example, it should write something like this:
> console.log(choose(data, 5));
[ { key: 'text15', value: 8 },
{ key: 'text6', value: 3 },
{ key: 'text3', value: 15 },
{ key: 'text19', value: 6 },
{ key: 'text8', value: 19 } ]
For now I'm extracting the keys into another array and sorting by Math.random() but I'm stuck at swaping the values because no key should have the same value it initially had.
How would you swap key/values here?
Thanks
I put together a possible solution using underscore.js to simplify traversing the object and arrays in a cross browser manner:
var data = {
text1: 1,
text2: 2,
text3: 3,
text4: 4,
text5: 5,
text6: 6,
text7: 7,
text8: 8,
text9: 9,
text10: 10
};
function choose(data, num)
{
var keys = _.sortBy(
_.keys(data),
function(k)
{
return (Math.random() * 3) - 1;
}
),
results = [],
k1, k2;
if (num > keys.length) {
throw new Error('Impossible to retrieve more values than exist');
}
while (results.length < num) {
k1 = k2 || keys.pop();
k2 = keys.pop();
results.push({key:k1, value: data[k2]});
}
return results;
}
console.log(choose(data, 5));
This isn't necessarily an optimal approach but it seems to meet your requirements. I first grab all of the keys and sort them randomly. I then loop through the random keys creating a new object with one key and the following keys value. That way you'll always end up with a different value associated with each key. If you need it to work when the value of num passed in to the function == the number of keys in the data then you'll have to add a little more code - I'll leave that as an exercise for the reader :)
You can have a play with this code on jsfiddle:
http://jsfiddle.net/zVyQW/1/
You could do this:
collect names and corresponding values in two arrays names and values
shuffle both arrays independently of each other
take the first n items of both arrays and combine them
Here’s an example implementation:
Array.prototype.shuffle = function() {
for (var i=this.length-1, j, tmp; i>0; i--) {
j = Math.round(Math.random()*i);
tmp = this[i], this[i] = this[j], this[j] = tmp;
}
return this;
};
function choose(data, number) {
var names = [], values = [], pick = [];
for (var name in data) {
if (data.hasOwnProperty(name)) {
names.push(name);
values.push(data[name]);
}
}
names = names.shuffle(), values = values.shuffle();
for (var i=Math.min(number >>> 0, names.length-1); i>=0; i--) {
pick.push({key: names[i], value: values[i]});
}
return pick;
}
Been a while since this was answered, but I was working on shuffling and found the following to be by far the fastest implementation with an evenly random distribution.
It's fast because it only makes one call to Math.random on each iteration, all the rest is done by property access. It doesn't modify the array, just reassigns values.
function shuffle(a) {
var t, j, i=a.length, rand=Math.random;
// For each element in the array, swap it with a random
// element (which might be itself)
while (i--) {
k = rand()*(i+1)|0;
t = a[k];
a[k]=a[i];
a[i]=t;
}
return a;
}
It uses a combination of three functions (including the Array shuffle prototype method).
Here is the complete code:
var obj = {
"red":"RED",
"blue":"BLUE",
"green":"GREEN",
"yellow":"YELLOW",
"purple":"PURPLE"
};
Array.prototype.shuffle = function(){
for (var i = 0; i < this.length; i++){
var a = this[i];
var b = Math.floor(Math.random() * this.length);
this[i] = this[b];
this[b] = a;
}
}
obj = shuffleProperties(obj); // run shuffle
function shuffleProperties(obj) {
var new_obj = {};
var keys = getKeys(obj);
keys.shuffle();
for (var key in keys){
if (key == "shuffle") continue; // skip our prototype method
new_obj[keys[key]] = obj[keys[key]];
}
return new_obj;
}
function getKeys(obj){
var arr = new Array();
for (var key in obj)
arr.push(key);
return arr;
}
for(key in obj){
alert(key);
}
Check all post,
Best Regards.
Use an implementation of random that randomizes a discrete set of values, such as Math.rand seen here. For each index, randomize Math.rand(index, length-1) to get a list of random indexes, the location off all indices will change.