Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about programming within the scope defined in the help center.
Closed 7 years ago.
Improve this question
I need a way to get the name of the variable with the greatest value.
a = 55;
b = 13;
c = 45;
d = 5;
var top = '';
if((a>b)&&(a>c)&&(a>d)){ top = 'a'; }
if((b>a)&&(b>c)&&(b>d)){ top = 'b'; }
if((c>a)&&(c>b)&&(c>d)){ top = 'c'; }
if((d>a)&&(d>b)&&(d>c)){ top = 'd'; }
Is there a better or faster way to do this?
You can't get the variable name directly:
Very few languages support what you want to do because the variable
names only exist for your benefit in the source code. They do not
exist in the compiled executable code in that form anymore and
tracking them would be extremely expensive.
If you want to do this, there is something fundamentally wrong with your design as there is no reason that would preclude doing it the most idiomatic way possible which is to use an associative array, which in JavaScript means using an Object or an actual Map when available and where appropriate.
Object based approach:
Most compatible way if you do not have access to Map:
You can use an object and find the property name. The most concise way to do this is with the Array.reduce() function.
var obj = {a:55,b:13,c:45,d:5};
var maxPropertyName = Object.keys(obj).reduce(function(previous,key){
return obj[previous] > obj[key] ? previous : key;
})
console.log(maxPropertyName);
Output:
a
Map based approach:
For this case a Map seems more appropriate since this looks like a homogeneousCollection rather than a Type of something.
Map instances are only useful for collections, and
you should consider adapting your code where you have previously used
objects for such. Objects shall be used as records, with fields and
methods. If you're still not sure which one to use, ask yourself the
following questions:
Are keys usually unknown until run time, do you need to look them
up dynamically?
Do all values have the same type, and can be used interchangeably?
Do you need keys that aren't strings?
Are key-value pairs often added or removed? Do you have an
arbitrary (easily changing) amount of key-value pairs?
Is the collection iterated? Those all are signs that you want a Map
for a collection.
If in contrast you have a fixed amount of keys, operate on them
individually, and distinguish between their usage, then you want an
object.
Here is how to add a .reduce() method to Map:
Map.prototype.reduce = function(callback){
'use strict';
if (this == null) {
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}
var t = Object(this), value;
if (t.size === 0) { value = undefined; }
else if (t.size === 1) { value = t.keys().next().value; }
else {
value = t.keys().next().value;
for (var kv of t) {
value = callback(value, kv[0]);
}
}
return value;
};
Same .reduce() code now works:
var m = new Map([["a",55],["b",13], ["c",45],["d",5]]);
var maxPropertyName = m.reduce(function(previous,key){
return m.get(previous) > m.get(key) ? previous : key;
})
console.log(maxPropertyName);
Output:
a
A simple way to do it, store everything in an object and loop over the keys:
// you can keep the input variables
var a = 55;
var b = 13;
var c = 45;
var d = 5;
var obj = {
a: a,
b: b,
c: c,
d: d
}
var max;
var varName;
Object.keys(obj).forEach(function(key) {
if (!max || max < obj[key]) {
max = obj[key];
varName = key;
}
});
snippet.log(max);
snippet.log(varName);
<script src="https://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
function high(obj) {
var highest = Number.NEGATIVE_INFINITY,
keyName = null;
for (var key in obj) {
if( obj[key] > highest ) {
highest = obj[key];
keyName = key;
}
}
return keyName;
}
In this way you can need not care about the variable name.Also need not repeat the variable name.
// init
var obj={};
var max=0,maxName="";
var x={};
for(var key in window){
x[key]=window[key];
}
// your code
a = 55;
b = 13;
c = 45;
d = 5;
// end of your code
// select the max num
for(var key in window){
if(typeof(window[key]) == "number" && typeof(x[key])!="number"){
if(window[key]>max){
max=window[key];
maxName=key;
}
}
}
// output the variable name
console.log(maxName);
And you can write a method to reuse:
function getTop(func){
// init
var obj={};
var max=0,maxName="";
var x={};
for(var key in window){
x[key]=window[key];
}
func();
// select the max num
for(var key in window){
if(typeof(window[key]) == "number" && typeof(x[key])!="number"){
if(window[key]>max){
max=window[key];
window[key]=undefined;
maxName=key;
}
}
}
// output the variable name
return maxName;
}
you can use the code to get the top varname:
var top=getTop(function(){
a=11;
b=22;
c=23;
d=14;
});
One way, if you cannot use object, then get max value and then check individual values to variable. Here you will not have very complex condition. Also its better to if...else if...else than multiple if
a = 55;
b = 13;
c = 45;
d = 5;
var max = Math.max(a,b,c,d);
var varName = "";
if(max === a){
varName = "a";
}
else if(max === b){
varName = "b";
}
else if(max === c){
varName = "c";
}
else{
varName = d;
}
alert(varName);
Related
I'm creating this Hash Table in JavaScript and I want it warn me each time I'm adding existing values in the array. I tried my own implementation, but it isn't working.
function hashFunction(s,tableSize){
let hash = 17;
for(let i = 0; i<s.length; i++){
hash = (13*hash * s.charCodeAt(i)) % tableSize;
}
return hash;
};
class HashTable {
table = new Array(2000);
numItems = 0;
loadFactor = this.numItems/this.table.length;
setItem = (key,value)=>{
const idx = hashFunction(key, this.table.length);
if(this.table.includes(key)){
return "already exists"
}else this.numItems++
if(this.table[idx]){
this.table[idx].push([key,value])
}else this.table[idx]=[[key,value]];
};
getItem = (key)=>{
const idx = hashFunction(key,this.table.length);
if(!this.table[idx]){
return "Key doesn't exist";
}else
return this.table[idx].find(x=>x[0]===key)[1];
};
};
let myHash = new HashTable
myHash.setItem("first","daniel")
myHash.setItem("last","esposito")
myHash.setItem("age","21")
myHash.setItem("height","1,90")
It would be beneficial to review implementing pseudo code and existing implementations. There were numerous errors which can summarized as the original implementation failing to correctly handle pairs within a bucket.
Here is a working update which salvages some of the structure while discarding most of the inconsistent and/or incorrect implementation - I've left comments as reference points to understand why the original was incorrect. The code has been structured to illustrate access/creation of buckets, access/creation of pairs within the buckets, and behavior selection depending on the case.
YMMV.
function hashFunction(s, tableSize){
let hash = 17;
for (let i = 0; i < s.length; i++){
hash = (13 * hash + s.charCodeAt(i)) % tableSize;
// ^-- Ry suggests the original * is a typo.
}
return hash;
};
class HashTable {
// I've eliminated 'load factor' to cover a basic implementation.
// See rebalancing, good table size selection, alternative collision
// resolution strategies, etc.
// This implementation might pass a CS101 course.
// Yes, for THIS EXAMPLE the TABLE SIZE IS CHOSEN AS 2.
// This ensures that there are multiple items per bucket
// which guarantees the collision resolution paths are invoked.
// This is a terrible choice in practice.
table = new Array(2);
numItems = 0;
setItem = (key, value)=>{
const idx = hashFunction(key, this.table.length);
// set/get should ONLY access simple properties or
// a BUCKET from the hash code as top-level structures.
// (Using table.includes(..) here is fundamentally INCORRECT.)
let bucket = this.table[idx];
if (bucket) {
// Existing bucket. Search in HERE to see if the key exists.
// This should generally be the same code as the "get".
let pair = bucket.find(x => x[0] === key);
if (pair) {
// Same pair, update value.
pair[1] = value;
return false; // "existing"
} else {
// Add new pair to bucket.
bucket.push([key, value]);
this.numItems += 1;
return true; // "new"
}
} else {
// Create a new bucket and item pair.
this.table[idx] = [[key, value]];
this.numItems += 1;
return true; // "new"
}
};
getItem = (key) =>{
const idx = hashFunction(key, this.table.length);
// Code should match close to 'set'
let bucket = this.table[idx];
if (bucket) {
let pair = bucket.find(x => x[0] === key);
if (pair) {
// Bucket exists and key exists within bucket.
return pair[1];
}
}
// The result should be the same if the key doesn't exist because
// bucket is not found, or if the bucket is found and the
// key does not exist within the bucket..
return undefined;
};
}
let myHash = new HashTable
var items = [
["first", "daniel"],
["last", "esposito"],
["age", 21],
["height", 1.9]
]
// Insert multiple values:
// Check to see inserted report true/not,
// and that the numItems is increased appropriately.
for (let run of [1, 2]) {
for (let i of items) {
let [k, v] = i;
var inserted = myHash.setItem(k, v);
var found = myHash.getItem(k) === v;
console.log(`run=${run} key=${k} value=${v} inserted=${inserted} numItems=${myHash.numItems} found=${found}` )
}
}
Output:
run=1 key=first value=daniel inserted=true numItems=1 found=true
run=1 key=last value=esposito inserted=true numItems=2 found=true
run=1 key=age value=21 inserted=true numItems=3 found=true
run=1 key=height value=1.9 inserted=true numItems=4 found=true
run=2 key=first value=daniel inserted=false numItems=4 found=true
run=2 key=last value=esposito inserted=false numItems=4 found=true
run=2 key=age value=21 inserted=false numItems=4 found=true
run=2 key=height value=1.9 inserted=false numItems=4 found=true
hash += (13*hash * s.charCodeAt(i)) % tableSize;
This is problem-solving q; nothing's gone wrong, I'm just stumped about how to advance. Basically I want my users to be able to
Point to an arbitrary key of an object with arbitrary depth via a string representation of the "path";
Confirm that each step of the "path" exists; and
Implement CRUD-like functionality
I can verify that each key is valid, but I'm just stumped on how to then utilize said path without ultimately using an eval() statement, but of course I needn't explain why I'm not going to let a call to eval() get anywhere near arbitrary user input. Here's as far as I can get:
const SEP = "/" //in reality this is set by the server,
MyObjInterface = function() {
this.params = {};
this.response = {};
// suppose some initialization business, then on to the question... ( >.>)
this.updateOb= function(path, value ) {
path = path.replace('\\' + DS,"$DIRECTORY_SEPARATOR").split(DS);
for (var i = 0; i < path.length; i++) {
path[i].replace("$DIRECTORY_SEPARATOR",DS);
}
if (typeof(path) != "object" || path.length < 3) { // 3 = minimum depth to reach a valid field in any rset scheme
throw "Could not create rset representation: path either incorrectly formatted or incomplete."
}
var invalidPath = false;
var searchContext = path[0] === "params" ? this.params : this.response;
for (var i = 1; i < path.length - 1; i++) {
if (invalidPath) { throw "No key found in rset at provided path: [" + path[i] + "]."; }
if (i === path.length - 1) {
searchContext[path[1]] = value;
return true;
}
if (path[i] in searchContext) {
searchContext = searchContext[path[i]];
} else {
invalidPath = true;
}
}
}
};
You break the path up in to components, then you recursively walk the tree.
My JS is weak, so I'll pseudo code.
path = "go.down.three";
listOfElements = breakUpPath(path); // Now listOfElement = ["go", "down", "three"];
myObj = setObjectValue(myObj, listOfElement, "I'm a value");
function setObjectValue(obj, listOfElements, value) {
var firstPart = pop(listOfElements); // get and remove top of listOfElements stack
if (length(listOfElements) == 0) { // last element of the path, set the value
obj[firstPart] = value;
return obj;
} else {
// check if a property exists at all
var firstValue = obj[firstPart];
if (firstValue == null) {
firstValue = new Object();
obj[firstPart] = firstValue;
}
obj[firstPart] = setObjectValue(firstValue, listOfElement, value);
}
}
So, my as I said, my JS is weak (really weak, I can't really even spell JavaScript). Needless to say, this is untested.
But something like that is what you're looking for. Just work your way down the elements of the path.
Hi all I'm learning Javascript with the Stoyan Stefanov's book. I'm stuck on Chapter 4 Exercise 4:
Imagine the String()constructor didn't exist. Create a constructor
function MyString()that acts like String()as closely as possible.
You're not allowed to use any built-in string methods or properties,
and remember that String()doesn't exist. You can use this code to
test your constructor:
>>> var s = new MyString('hello');
>>> s[0];
"h"
I can't think on a way to achieve "s[0]", at least not with the knowledge I have now.
Any thoughts?
Thanks
Objects can have properties of themselves defined using array like syntax. String chars can be accessed with array like syntax.
function MyString (str) {
this.length = 0; // string length
var i = 0;
while(str[i] != undefined) {
this.length++;
i++;
}
for (var i=0; i< this.length;i++)
{
this[i]=str[i];
}
}
var s=new MyString('hello');
alert(s[0]); //h
here is my solution for this exercice :
function MyString(msg){
var array_msg = msg.split("");
array_msg.toString = function(){
return array_msg.join("");
};
array_msg.valueOf = function(){
return array_msg.toString();
};
array_msg.charAt = function(i){
if(array_msg[i] === undefined){
return array_msg[0];
}else{return array_msg[i];}
};
array_msg.concat = function(msg2){
return array_msg.join("")+" "+msg2;
};
array_msg.slice = function(d,f){
var res = "";
if(f<0){
f = array_msg.length + f;
}
for(var i=d; i<f; i++){
res += array_msg[i]
}
return res;
};
array_msg.split = function(el){
return array_msg.toString().split(el);
};
return array_msg;
}
A slight variation of the above...more of a tweak than anything
var MyString = function (s) {
for (var i = 0; i < s.length; i++){
this[i] = s[i]
}
this.length = function() .....
You also don't need to assign it to anything as extra as the comment suggests. this[i] will be created for the length of the string passed to s
EDIT:
Part of the question in the book says not to use existing string methods so can't use charAt so I've switched it to s[I]
This is another variation of one of the above solutions but instead of using a for loop I am using a while loop. I don't usually use while loops for these kinds of things but it worked really well here.
Adding the length property is optional.
function MyString(str) {
this.length = 0; // Creating an optional length property
this.value = str;
var i = 0;
while(str[i] != undefined) {
this[i] = str[i];
this.length++;
i++;
}
}
var name = new MyString('billy');
console.log(name.value); // 'billy'
console.log(name[0]); // 'b'
console.log(name.length); // 5
I have a log analysis script to populate a complex visualisation.
Picture an array (called, rather unoriginally, 'log') of Activity objects, each of which is in the form:
{
name:foo,
activities:[
{time:t, action:a},
{time:t, action:a},
{time:t, action:a},
...
]
}
There will be up to 75 activity objects in the array, each containing an array of 400-600 actions (one timeslot every 5 minutes since midnight the previous day).
Given a known activity name (foo above) and a time that will already exist in the activities array, I need to update the associated action.
Each name will be unique and each time is in ascending order in the array in exact 5 minutes increments.
As I have to do this a little over 1000 times every time the graph is updated (so an average of 1000 values to update and 1000*500*60 points to plot), performance is a fairly key issue...
Looping in jq is far more efficient than anything I could write so, at the moment, I have
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
$.grep($.grep(log, function(n, i)
{
return (n.name == n)
}
)[0].activities, function(n, i)
{
return (n.time == t)
}
)[0].action = "bar";
That seems to be working, but it's taken me so long and I've had so many arguments with myself that I'm not confident.
Am I missing a better way?
I wont give you a better loop method for your problem, as any loop you come up with will relatively be no better than the last.
If you truly want a solution that will enhance performance, you should think about rearranging your object entirely. If every name of each log and time of each activities array is unique, you can change your object setup to have those values as the key of each subobject.
Using this method, you'll just be doing a key look up, no loop needed.
New LOG Object
var log =
{
unique_name : {
"activities" : {
time_1 : action_1,
time_2 : action_2,
time_3 : action_3,
etc...
}
},
unique_name_2 : {
"activities" : {
etc...
}
}
}
Now with var u_name = "foo"; and var t = "some time"; you can simply do...
log[u_name][t] = "some action";
Hope this helps!
Seems like you want the first matched activity of the first matched log.
In that case, you should break the loop after the first match is found. You can do this with .some().
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
log.some(function(ob, i) {
if (ob.name == n) {
ob.activities.some(function(ob2, i) {
if (ob2.time == t) {
ob2.action = "bar";
return true;
}
});
return true;
}
});
Also, your n parameter was shadowing your n variable, so I changed the param to ob.
But for loops will generally be quite a bit faster than functional methods.
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
for (var i = 0; i < log.length; i++) {
var ob = log[i];
if (ob.name == n) {
for (var j = 0; j < ob.activities.length; j++) {
var ob2 = ob.activities[j];
if (ob2.time == t) {
ob2.action = "bar";
break;
}
}
break;
}
}
If you decide that you should keep the outer loop going if there's no match found on the inner loop, change the code to one of these:
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
log.some(function(ob, i) {
if (ob.name == n) {
return ob.activities.some(function(ob2, i) {
if (ob2.time == t) {
ob2.action = "bar";
return true;
}
});
}
});
n = "foo";
t = new Date(y,mm,d,h,m).toLocaleString() // matches a time stamp in the log
OUTER:
for (var i = 0; i < log.length; i++) {
var ob = log[i];
if (ob.name == n) {
for (var j = 0; j < ob.activities.length; j++) {
var ob2 = ob.activities[j];
if (ob2.time == t) {
ob2.action = "bar";
break OUTER;
}
}
}
}
i've created a short function to set and retrieve values from a object (> get points by name), but I'm not sure if my solution is that really smart.
What modifications do you recommend to perfect this query?
var points = {}; //global var
function getPoints() {
var args = arguments, name;
// set points or return them!
if (typeof args[0] == 'object') {
name = args[0].name;
points = { name: args[0].points };
} else {
name = args[0];
return points.name;
}
}
//set:
getPoints(name: "John", points: 0) //set points (0)
getPoints(name: "Pamela", points: 2 ) //set points (2)
//return:
getPoints("John") //returns > (0)
getPoints("Pamela") //returns > (2)
[edit] previous answer was wrong: didn't mention the impossibility of getPoints("John")
As far as I can see, you are trying to combine get and set in one function.
You may want to use a Points constructor function here, something like:
var Points = function(name,points){
this.name = name || '';
this.points = points || 0;
if (!Points.prototype.get){
var proto = Points.prototype;
proto.get = function(label) {
return this[label] || label
};
proto.set = function(){
if (arguments.length === 2){
this[arguments[0]] = arguments[1];
} else if (/obj/i.test(typeof arguments[0])){
var obj = arguments[0];
for (var l in obj){
if (obj.hasOwnProperty(l)){
this[l] = obj[l];
}
}
}
return this;
};
}
}
var john = new Points('John',0), mary = new Points('Mary',2), pete = new Points;
pete.set({name:'Pete',points:12});
john.set('points',15);
//two ways to get a 'points' property
alert(john.get('points')+', '+pete.points); //=> 15, 12
I would create a different function for the getter and the setter.
A function called getPoints that also sets points doesn't make any sence and would confuse ppl :)
The only problem I see is that the value of points gets overwritten every time you call getpoints i.e:
getPoints({name :"John", points: 0}); // points = {"John": 0}
getPoints({name:"Mein", points:1}); // points = {"Mein":1}
Also the name is confusing. Mine verison would be:
var points = {};
function getOrSetPoints(){
if(typeof arguments[0] === "object"){
points[arguments[0].name] = arguments[0].points;
}
else
return points[arguments[0]] || arguments[0] + ' not set'
}