Before marking this as a duplicate, i've spent a lot of time looking through similar question and most of the answers did not solves my situation.
i have a huge list of items as objects by IDs. like this, in a Map (userDB)
{
"15321":{name:"name1",status:"status1"},modules:{...},
"15322":{name:"name1",status:"status1"},modules:{...},
"15323":{name:"name1",status:"status1"},modules:{...}
}
now i need to make an operation in which i need all these IDs, in that case, the key names of every item on it. i need to get these "15321","15322" etc.
more specifically i wanted something that i could fetch in something like
userDB.forEach( u => {
//something here to get u's key
})
i've tried Object.keys(), but it will return a list of IDs as an object
{"15321","15322"...} in which i still cant grab the ID string
i've tried for (i in Object.keys(userDB)) too, no successs
i double-checked for silly syntax errors and everything of the sort.
Things that will be nice to get in mind to answer this:
dont try to show me a new way of storing stuff, it is already stored so you will not be of help
the result SHOULD be the ID as a string, the name of the key.
dont ask "why i want to make this". just answer and dont try to change this scenario. because this is what i've seen in most of the other similar questions and it is what makes me walk in circles every time.
TL;DR. i just want to get the parent key names of the object im currently processing
Object.keys(obj) will return an array.
But in your data there is another key modules except IDs.
So you can use this :
var keys = Object.keys(data);
keys.pop();
console.log(keys); // ["15321", "15322", "15323" ...]
You might be confused. Object.keys(obj) returns an array. In your case it looks like this: ["15321", "15322", "15323"]. You could iterate through that array like so and you'll have both the key and the object and you'll be able to do with them whatever you want. Below is a for loop that attaches the key to the object as a key named 'key'.
var keys = Object.keys(myObject);
for(var i = 0; i < keys.length; i++){
var key = keys[i]; // this is your key
var obj = myObject[key]; // this is the key's value
obj.key = key;
}
EDIT
In javascript an Array is also an object, but the 'keys' so to speak are usually numbers instead of strings. So an array that looks like this:
["Hello", "there"] is essentially represented like this: { 0 : "Hello", 1 : "there" }
When using for-in on an array, it'll loop through the keys, but those keys will be 0, 1, 2... instead of the items themselves.
Related
I am looking for quick way to get an item from the Mozilla localStorage based on partial knowledge of an unique key. I have the following structure defined as the key : value
UUID1 : "BLABLABLA": UUID2 : Value
Where the key part looks like this for example:
ca4417f4-f83b-11eb-bf45-ffc420139ccb:BLABLABLA:e1d26c40-f83b-11eb-81c0-fbcc16bf6bdb
Value is just a long string.
What would be the fastest way to find a SINGLE key from localStorage based on only one of the two UUIDs i.e. either UUID1 or UUID2, then obtain the full key string and then use
localStorage.getItem to return it's value
maybe using Object.entries(localStorage)?
a code example would be really appreciated?
The Storage interface has a length property telling you how many items are there, and a key method that gives you the key for item index n. So you can loop through to find the key with a for loop:
let key;
for (let n = 0, len = localStorage.length; n < len; ++n) {
const thisKey = localStorage.key(n);
if (thisKey.includes("BLABLABLA")) {
// found it
key = thisKey;
break;
}
}
if (key) {
const value localStorage.getItem(key);
// ...
}
Note that solutions using Object.keys, Object.entries, etc. won't work reliably with all storage keys — for instance, the key "length" won't work properly even though it's just fine to use "length' as a key with getItem and setItem. That's because the Storage interface already defines a property called length (the length of the storage list), so you can't access a stored item keyed by "length' using localStorage.length, you have to localStorage.getItem("length"). Object.keys and Object.entries will leave out those storage entries. (Other than it appears there's a weird bug in Chrome around the "key" key.) The above works reliably with length, key, and other similar keys (but really it's length and key that are the most problematic).
In your particular case, though, you know the key isn't length or key or any of the other things on Storage.prototype, so you could create an array of keys via Object.keys and use find to find the key:
// Read the disclaimers above
const key = Object.keys(localStorage).find(key => key.includes("BLABLABLA"));
if (key) {
const value localStorage.getItem(key);
// ...
}
...or use Object.entries to create an array of arrays as charlietfl shows. Just be mindful of the caveat.
Expanding on your Object.entries() concept you can do something like:
// set a demo key/value
localStorage.setItem('foobar', 100);
const [wantedKey, wantedValue] = Object.entries(localStorage).find(([k,v])=> k.startsWith('foo'))
console.log([wantedKey, wantedValue])
Working jsfiddle
I'm renaming the keys of an object while iterating them:
Object.keys(object).forEach(function(oldKey) {
var newKey = someFunc(oldKey);
if (newKey !== oldKey) {
object[newKey] = object[oldKey];
delete object[oldKey];
}
}
And I would like to know if this method is safe.
In other words, can I be sure that my code will never iterate a key which has been renamed in a previous iteration?
No, you aren't safe. You're mutating the object live, based on an array that is not live. If you happen to cross a new name with an old (rename a to b, but b already exists and haven't been reached yet) you're going to have a bad time.
You will not come across keys you've already seen, but you have no way to know whether the newKey is not already found in the object.
There are workarounds, the situation is similar to .splice()ing an array (removing elements) while you iterate it, and the simple workaround is to iterate backwards, so that you always already pass the altered keys. (Or in your case, checking with the in operator)
You're much better, however, creating and returning a new object:
const newObj = Object.keys(object).reduce(function(result, oldKey) {
var newKey = someFunc(oldKey);
return { ...result, [newKey]: object[oldKey] };
}, {});
You get a lot of things for free when you treat all of your data structures as immutables (and more specifically, when the keys never change)
Object.keys, like many other methods, returns an Array that you can iterate over. This Array is not "live" but a snapshot from the time of taking it (e.g. executing Object.keys). So yes, you're save to use it as intended.
There are very little examples of methods that return "live lists" instead of an Array; I guess you're having NodeLists in mind, that you'll get when using document.querySelectorAll. This however not an Array but a NodeList.
However, there may be one pitfall I can see is: When a generated newKey already exists in the list of oldKeys (not the current one!). So you may or may not (depending on the position in the array) iterate over the already overwritten new key.
Here is a solution to change the key without creating a new Object.
for(key in obj){
Object.defineProperty(obj, `myNewName`, Object.getOwnPropertyDescriptor(obj, key));
delete obj[key];
}
I've been teaching myself OOJS by creating a blackjack game. Everything is going great, but since I randomly create cards, sometimes I create and then deal the same card twice in a round. I'm trying to avoid that by writing some logic that gets rid of duplicate cards.
So I found this discussion.
https://stackoverflow.com/a/840849/945517
and the code below:
function eliminateDuplicates(arr) {
var i,
len=arr.length,
out=[],
obj={};
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
for (i in obj) {
out.push(i);
}
return out;
}
var a=[];
var b=[];
a[0]="fish";
a[1]="fishes";
a[2]="fish";
a[3]="1";
a[4]="fishes";
b=eliminateDuplicates(a);
console.log(a);
console.log(b);
I understand what's going on in general and on almost every line except:
for (i=0;i<len;i++) {
obj[arr[i]]=0;
}
It seems like it's looping through the array and setting the key of obj to zero. What's going on here and how does this help get rid of duplicate entries in the array being passed at the start?
Thanks!
{} = {key: value, key2:value2,....}
The above is essentially a key value map. The code iterates through the array and adds the key into the map with a value of zero. When the map tries to access an existing value, all it does is reset the value to zero. A useless operation, but it avoids an if..else or other more complex logic.
Once the array is done being iterated across, you only need to iterate across the key value map to get the keys. Since the keys are guaranteed to be unique, you have a list of unique items.
The main thing to realize here is that an object can only have one property per unique string, so when you set obj.fish to 0 when i = 0, you don't add a second "fish" property to obj when i = 2.
Then you can just loop through all of the properties of obj, each one is guaranteed to be unique, and thus you have stripped duplicates.
I have some simple Javascript looping through an array of items (Tridion User Groups) to check if the user is a member of a specific group.
I can easily code around the issue shown below ( see && extensionGroup !== 'true') but I want to understand why the isArray = true is counted as a value in the array - any ideas?
The screenshot below demonstrates that the value extensionGroups has been set thus
var extensionGroups = ["NotEvenARealGroup", "Author", "ExampleGroupAfterOneUserIsActuallyIn"];
but returns the isArray value as a 4th value?
updated to show images a little clearer
You're using for in to iterate an array; don't do that. Use for (or forEach):
for(var i = 0; i < extensionGroups.length; i++) {
var extensionGroup = extensionGroups[i];
// ...
}
The reason this fails is because for in is used to iterate over an object's properties in JavaScript. Iterating over an array in this way means you get anything else assigned to it, such as this property or length.
And if you're able to use Array#forEach, it's probably most appropriate here:
extensionGroups.forEach(function(extensionGroup) {
// ...
});
For..in, technically speaking, doesn't iterate through values. It iterates through property names. In an array, the values ARE properties, under the hood. So when you iterate over them with for..in you get funky stuff like that happening.
Which highlights my next point: don't use for..in. Don't use it for arrays -- don't use it for anything, really. Ok -- maybe that's going a bit too far. How about this: if you feel the need to use for..in, think hard to see if it's justifiable before you do it.
I know this isn't the best way to do it, but I have no other choice :(
I have to access the items in JSONObject by their index. The standard way to access objects is to just wirte this[objectName] or this.objectName. I also found a method to get all the fields inside a json object:
(for (var key in p) {
if (p.hasOwnProperty(key)) {
alert(key + " -> " + p[key]);
}
}
(Soruce : Loop through Json object).
However there is no way of accessing the JSONfields directly by a index. The only way I see right now, is to create an array, with the function above, get the fieldname by index and then get the value by fieldname.
As far as I see it, the p (in our case the JSON file must be an iteratable array to, or else the foreach loop wouldn't work. How can I access this array directly? Or is it some kind of unsorted list?
A JSON Object is more like a key-value-map; so, yes, it is unsorted. The only way to get around is the index->property name map you've already mentioned:
var keysbyindex = Object.keys(object);
for (var i=0; i<keysbyindex.length; i++)
alert(object[keysbyindex[i]]);
But why would you need these indexes? A unsorted map also has no length property, as an Array had. Why don't you use the for-in-loop
var counter = 0; // if you need it
for (var key in object) {
alert(object[key])
counter++;
}
? If you have a parsed JSON object, i.e. a plain JS Object, you won't have to worry about enumerable prototype properties.
Based on Bergis anserwer this is my solution:
var keysbyindex = Object.keys(this);
alert(this[keysbyindex[index]]);
return this[keysbyindex[index] || ""];
However, I think (not tested) it's extremly bad regaring performace and shouldn't be used! But desperate times require desperate measures.....
I don't think you can actually achieve this without creating your own parsing of JSON. You're writing that you want to go trough a JSON-object, but what you're actually trying to do is go trough a plain old Javascript object. Json is simply a string-representation used to transfer/store said object, and in here lies the main problem: the parser that transforms the string into an actual object (ie. the browser in most cases) can chose to ignore the order it finds the properties if it want to. Also, different browsers might have different approaches to parsing JSON for all you know. If they simply use a hash-map for the object that it's simple to loop through it, but the order won't be dependent on the order of the keys in the file, but rather the keys themselves.
For example, if you have the json {"b":"b","a":"a"} and do the for in loop, under some implementations you might end up with a comming first, and in others you might end up with b.
var jsn = {keyName: 'key value result come here...'};
var arr = jsn ? $.map(jsn, function (el) { return el }) : [0];
console.log(arr[0])
$('.result').text(arr[0]);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="result"></span>