Related
I have searched and searched and cannot find an answer to this, so please accept my apologies if I am being foolish.
I am building an application in node.js that uses JSON files as project configs. I am using the 'fs' module to read the JSON file, which I then parse into a Javascript object. I then amend details within the object before using JSON.stringify to write the data back to the file.
My problem is that I need to delete an array, not just empty it, in order to show the data correctly on my interface:
"floors": [
{
"floorName": "Grd Floor",
"floorNotes": "",
"floorPlan": "Buckley_Grd.jpg",
"floorWidth": "57.392",
"floorHeight": "20.776",
"runs": [
[], *<----I cannot delete these*
[] *<----only empty them*
[
{
"xCoOrd": "37.88235294117647",
"yCoOrd": "59.307359307359306",
"drawingType": "node"
},
{
"xCoOrd": "37.88235294117647",
"yCoOrd": "59.307359307359306",
"drawingType": "node"
},
{
"xCoOrd": "48.549019607843135",
"yCoOrd": "50",
"drawingType": "node"
}
] *<----I don't want to delete the elements in this array*
]
}
I have tried:
.splice(0);
.filter(Boolean);
delete
Everywhere I have looked people seem surprised that one would want to delete an unused array from memory, which is what I beleive I am trying to do. Perhaps there is a way to stringify the object that ignores these empty arrays? Or perhaps I am doing this completely the wrong way?
Many thanks in advance for any advice offered.
EDIT: I should have made clearer that I don't necessarily want to delete all of the elemnets in the array so redeclaring floors.runs =[] unfortunately will not help me.
I have tried:
delete floors[floorID].runs[runID];
This replaces the array with null which gets written to the file and ruins my reading of the JSON.
Did you try over-writing the 'runs' property of each object? If you said floors[0].runs = [] (or floors[i].runs = [], if you're looping through), then those two empty arrays would no longer show up in your stringify.
Editing my answer, to match the OP edits:
floors.forEach(function(floor){
var newRuns = [];
floor.runs.forEach(function(run){
if (run.length > 0){
newRuns.push(run);
}
})
floor.runs = newRuns;
})
After I assign your object from the OP into a variable and run my code on it, then stringify the result, I get this as a return:
{"floors":[{"floorName":"Grd Floor","floorNotes":"","floorPlan":"Buckley_Grd.jpg","floorWidth":"57.392","floorHeight":"20.776","runs":[[{"xCoOrd":"37.88235294117647","yCoOrd":"59.307359307359306","drawingType":"node"},{"xCoOrd":"37.88235294117647","yCoOrd":"59.307359307359306","drawingType":"node"},{"xCoOrd":"48.549019607843135","yCoOrd":"50","drawingType":"node"}]]}]}
Is that what you're hoping to see? I used this fiddle to produce that result.
If you want remove ALL values from runs array you simply do this:
floors[i].runs = null
If you want remove only the empties
var filled = [];
obj.floors[i].runs.forEach(function(value, i, array) {
if (value.length != 0) filled.push(value);
});
obj.floors[i].runs = filled;
Runs is a property of an object, you can remove items from that array. In your example above floors[0].runs.splice(0,2), should remove both of those arrays I think.
Keeping the same reference
Iterating downwards lets you safely perform operations which decrease the length of an Array by 1 each time
var i, parentArray = obj['floors'][0]['runs'];
for (i = parentArray.length; i >= 0; --i) {
if (parentArray[i].length === 0) parentArray.splice(i, 1);
}
Making a new reference
Creating a new array without the values you don't want can be achieved using .filter
obj['floors'][0]['runs'] = obj['floors'][0]['runs'].filter(function (x) {
return x.length > 0;
});
To remove an object completely you have to remove the reference from the parent. To do this what you can do is create a new Array in which you copy only the references to the non-empty arrays.
I have created this function which should remove all empty arrays. I haven't tested it too much, but this should give you an idea:
function clearEmptyArrays(obj) {
for(var key in obj) {
var arr = obj[key];
if(Array.isArray(arr)) {
// Iterate through all elements and remove empty arrays
var nonEmptyArr = [];
for(var i = 0; i < arr.length; ++i) {
// Recursive call if it's an object inside an array
if(typeof arr[i] === 'object') clearEmptyArrays(arr);
// Save all non-empty arrays or objects inside this one
if((Array.isArray(arr[i]) && arr[i].length > 0) || !Array.isArray(arr[i])) {
nonEmptyArr.push(arr[i]);
}
}
obj[key] = nonEmptyArr;
return;
}
// Recursive call if it's an object
if(typeof arr === 'object') {
clearEmptyArrays(arr);
}
}
}
You can call it like this: clearEmptyArrays(myObj); and will update the Object in-place.
Demo: https://jsfiddle.net/zeo7dauh/
update
I have adjusted/corrected the example objects, because they contained an error before.
I have an mapping object that looks like this:
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
};
which allows to map one way i.e. AtoB["love"] yields "amore". I could add an inverse to it manualy i.e.
var BtoA = {
"love": "amore",
"food": "alimenti",
"beer": "Bier"
};
Anyway it troublesome the two objects in sync and I would like to create the BtoA programmatically in Javascript. Is there some sort of function xyz() which yields var BtoA = xyz(AtoB);?
The example above can be extended to include a problem (e.g. if I have too much "beer")
var AtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer"
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
as this is not a 1-to-1 mapping. In amateuer math terms It is not an inversible function?
To make things even more complicated I have a recipe for desaster.
var makeStuff = {
"agriculture": "food",
"hops" : {
"water": {
"malt": "beer"},
"malt": {
"water": "beer"}},
"water" : {
"hops": {
"malt": "beer"},
"malt": {
"hops": "beer"}},
"malt" : {
"water": {
"hops": "beer"},
"hops": {
"water": "beer"}}
};
inversing this nested javascript object, seems even more challanging for such an xyz() function. Anyway maybe there is such an xyz() function, then I would be glad to accept this as an answer to this question
Very simple. Following is the code to inverse key, value.
var inverse= (function inv(param){
for(var attr in param) {
if(param.hasOwnProperty(attr)) {
if(typeof param[attr]==="string") {
param[param[attr]] = attr;
delete param[attr];
} else if (Object.prototype.toString.call(param[attr]) === "[object Object]") {
param[attr] = inv(param[attr]);
}
}
}
return param;
});
To get the result into other object, initialize it with empty and assign it. Like
var BtoA = {};
BtoA = inverse(AtoB);
And, The JSON:
var AtoB = {
"love": "amore",
"food": "alimenti",
"beer": "Bier",
"beer": "cerveza",
"beer": "pivo",
"beer": "birra",
"beer": "cerveja",
};
has only three attributes because JSON is a dictionary data structure: new key will replace the old one. So the above JSON will actually be like:
{love: "amore", food: "alimenti", beer: "cerveja"}
So, inverting the above given JSON (AtoB) will result in the inversion of only three properties, and final result will be:
{amore: "love", alimenti: "food", cerveja: "beer"}
The answer from Muhammad imran is effective if the purpose/target is a simple inversion (i.e. no nested object structure; no multiple values).
Obviously that is the best result to be achieved, if no further artifice is created, to cover the fact that the key->value relation in objects are:
keys are unique,
values can be muliple.
Looking at the beer example above it is somewhat regretible that the information is lost in the inversion. Therefore this answer should supplement and enrich and provide a way in which the information can be stored. The way to achieve it is using Javascript Arrays within the resulting inverted object, to allow to store the potentially ambigious new values. as for example.
var BeerAtoB = {
"amore" : "love",
"alimenti": "food",
"Bier" : "beer",
"cerveza" : "beer",
"pivo" : "beer",
"birra" : "beer",
"cerveja" : "beer"
};
allowing to translate (de,es,pl/cz,it,pt)"beer" to English would best store
this information in the inverted too
var BeerBtoA = {
"love" : "amore",
"food" : "alimenti",
"beer" : [ "Bier" ,
"cerveza",
"pivo",
"birra",
"cerveja"
]
};
a version in which less information get lost and the multipleness of the original value "beer" is reflected by multipleness of values under the joint, inverted key "beer" now.
To accomplish this I made an enhanced inverting function
function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject; function invertObject(obj)
{
var invertedObject = {};
// make a stack and prime it with the obj
var stack = [];
stack.push({"way":[],"obj":obj});
// while stuff on the stack
while (stack.length)
{
var way= stack[0].way;
var obj= stack[0].obj;
for (var prop in obj)
{
if (typeof obj[prop] === 'object')
{
// attributes, which are themselves objects are added to the stack,
// with their way information.
stack.push({"way":way.concat(prop),"obj":obj[prop]});
}
else
{
// always start with adding things to the invertedObject,
var curobj = invertedObject;
var value = newKey = obj[prop];
var curpath = way.concat(prop).concat(obj[prop]);
// for all but the last two path elements the loop below
// will create the inverted path, starting with the value (obj[prop])
// as key, Since values need not be unique (as keys), create each
// such new key-property as an Array, not to loose inverted pathes.
while(curpath.length>2)
{
var pathpart = curpath.pop();
if(!curobj.hasOwnProperty(pathpart))
{
curobj[pathpart]=[];
}
curobj=curobj[pathpart];
}
// the last two curpath Array members represent the last key and the
// new to be added value.
var preLastPart = curpath.pop();
var lastPart = curpath.pop();
// Again the artifice of an Array is used since
// the inverted keys are not unique, hence cases in which
// 1 key has (>1) values.
if(!curobj.hasOwnProperty(preLastPart))
{
curobj[preLastPart]=[];
}
curobj[preLastPart].push(lastPart);
}
}
stack.shift();
}
return invertedObject;
}
}
Indeed since equal values can be found in many places of simple and nested object, the result will be an object in which each value will be an Array for two reasons:
Several original object's keys, can have the same value, and therefore (after inverting) more than one value can exists. An Array can store all those multiple new values, hence all information.
While in an nested object, the uniqueness makes every property either a direct value or a subobject, in the inverted object at a key, we can find not only muliple values, but also that at the very same place there are also further nested objects. (For this reason it is lucky that an Javascript Array, as being an Object, does besides its entry allow also for further properties to be attached to it and hence can serve simultaneously as a storage for the multiple values and as a subkey in the nested structure. Such a double purpose of Arrays in the inverted object structure, is unforunatelly hard to show in JSON notation, as the JSON notation does not allow for Arrays with Object Attributes)
Title is pretty much self explanatory...
I want to be able to find duplicated values from JavaScript array.
The array keys can be duplicated so I need to validate only the array values.
Here is an example :
var arr=[
Ibanez: 'JoeSatriani',
Ibanez: 'SteveVai',
Fender: 'YngwieMalmsteen',
Fender: 'EricJohnson',
Gibson: 'EricJohnson',
Takamine: 'SteveVai'
];
In that example:
the key is the guitar brand
the value is the guitar player name.
So:
If there is duplicated keys (like: Ibanez or Fender) as on that current example that is OK :-)
But
If there is duplicated values (like: EricJohnson or SteveVai) I'm expecting to get (return) that error:
EricJohnson,SteveVai
You can't have associative arrays in Javascript. You can create an array of objects, like:
var arr=[
{Ibanez: 'JoeSatriani'},
{Ibanez: 'SteveVai'},
{Fender: 'YngwieMalmsteen'},
{Fender: 'EricJohnson'},
{Gibson: 'EricJohnson'},
{Takamine: 'SteveVai'}
];
Then you'll need a for...in loop to go over every object in the array, create a new array of values and check that for duplicates, which is also not very straightforward - basically you'll want to sort the array and make sure no value is the same as the one after it.
var arrayOfValues = [];
arr.forEach(function(obj){
for(var prop in obj)
arrayOfValues.push(obj[prop]);
});
arrayOfValues.sort(); // by default it will sort them alphabetically
arrayOfValues.forEach(function(element,index,array){
if(array[index+1] && element==array[index+1])
alert("Duplicate value found!");
});
First of all, object keys can not be repeated.
This means that:
({
"Fender": "Jim",
"Fender": "Bob"
})["Fender"]
Would simply return: "Bob".
However, I did make a code that could allow you to find duplicates in values, but as I said, the key will have to be unique:
var arr = {
Ibanez: 'EricJohnson',
Fender: 'YngwieMalmsteen',
Gibson: 'EricJohnson',
Takamine: 'SteveVai',
"Takamine2": 'SteveVai'
};
function contains(a, obj) {
for (var i = 0; i < a.length; i++) {
if (a[i] === obj) {
return true;
}
}
return false;
}
var track = [];
var exists = [];
for (var val in arr) {
if (contains(track, arr[val])) {
exists.push(arr[val]);
} else {
track.push(arr[val])
}
}
alert(exists)
You can see it working here: http://jsfiddle.net/dr09sga6/2/
As others have commented, the example array you provided isn't a valid JavaScript array. You could, however, keep a list for each guitar type:
var mapping = {
Ibanez: ['JoeSatriani','SteveVai'],
Fender: ['YngwieMalmsteen','EricJohnson']
Gibson: ['EricJohnson'],
Takamine: ['SteveVai']
];
Or a list of each guitar/musician pair:
var pairs = [
['Ibanez','JoeSatriani'],
['Ibanez','SteveVai'],
['Fender','YngwieMalmsteen'],
['Fender','EricJohnson'],
['Gibson','EricJohnson'],
['Takamine','SteveVai']
];
Your solution is going to depend on which pattern you go with. However, in the second case it can be done in one chained functional call:
pairs.map(function(e) {return e[1]}) // Discard the brand names
.sort() // Sort by artist
.reduce(function(p,c,i,a){
if (i>0 && a[i]==a[i-1] && !p.some(function(v) {return v == c;})) p.push(c);
return p;
},[]); //Return the artist names that are duplicated
http://jsfiddle.net/mkurqmqd/1/
To break that reduce call down a bit, here's the callback again:
function(p,c,i,a){
if (i>0
&& a[i]==a[i-1]
&& !p.some(function(v) {
return v == c;
}))
p.push(c);
return p;
}
reduce is going to call our callback for each element in the array, and it's going to pass the returned value for each call into the next call as the first parameter (p). It's useful for accumulating a list as you move across an array.
Because we're looking back at the previous item, we need to make sure we don't go out of bounds on item 0.
Then we're checking to see if this item matches the previous one in the (sorted) list.
Then we're checking (with Array.prototype.some()) whether the value we've found is ALREADY in our list of duplicates...to avoid having duplicate duplicates!
If all of those checks pass, we add the name to our list of duplicate values.
I've searched and found a few similar-but-not-quite answers.
I have an array SongList (showing 2 items for brevity...) - the first pair is a key, the second pair is some JSON.
SongList={
song_1:{title:"title of first song",artist:"the artist",mp3:"http://mysite/song1.mp3"},
song_2:{title:"title of second song",artist:"the artist",mp3:"http://mysite/song2mp3"}
...
};
I would like to be able to retrieve the key (song_1 or song_2) given the title in the value.
I will be looping through a temporary array of i items, each item in this array would have a match in SongList and I would save the key (song_1, song_2) in a final array.
You don't have an array, you have an object, containing more objects. Use for in
function findTitle(title) {
for (var key in SongList) {
if (SongList[key].title == title) return key;
}
return false;
}
And then call it!
findTitle("title of first song"); //returns "song_1"
findTitle("BNOT MEEEEEE"); //returns false
Here is an example.
var one = {
a: {b:"MightyMouse", d:2},
b: {b:"MickeyMouse", d:4},
c: {b:"Superman", d:6}
};
for (outerkey in one) {
if (one[outerkey].b === "Superman") {
console.log ("Outerkey = " + outerkey);
}
}
Assuming you are looking for Superman, this prints as expected c.
Thanks everyone, I realize my understanding of Arrays v. Objects was an issue which obviously hindered my Google-Fu. I hope it's ok to post the answer I finally arrived at via guidance here:
(SongList object is described in question above)
This is the eventual function I arrived at for saving the keys of the playlist SongList:
$("#save_playlist_as_keys").click(function() {
var keys = []
for(var i=0; i<myPlaylist.playlist.length; i++){
var playItem = (myPlaylist.playlist[i].title); //this returns the names of the tracks
for (var k in SongList){
if(SongList[k].title == playItem) keys.push(k);//this matches track name to keys
}
}
localStorage.setItem('storedKeys',keys);
});
This seems to be doing what I want for now.
I currently have a list of objects in javascript indexed by a key:
var list = [];
list['a'] = [];
list['a'].push({obj: 'test'});
list['a'].push({obj: 'test2'});
list['b'] = [];
list['b'].push({obj: 'test'});
list['b'].push({obj: 'test2'});
I would list to remove the entry based on the key (a/b)
I have tried the following:
for(var x in list) { delete list[x]; }
that works but it actually leaves an undefined entry in the list.
I have also tried splicing the array, but that does not seems to work in this case.
Any thoughts on how to remove the entry in javascript or jQuery?
Thanks.
The Fix:
After reading some of the comments, i was able to better understand what my list is consistent of. Therefor, i was able to do the removal by doing the following:
delete list.b;
I'm not sure if my list is best way to organize my structure, but doing a delete on the list and treating it like an object property did the trick.
Thanks for all the feedback.
I'll assume list is an object, not an array.
If you want to reset a or (or b it's done the same way)
list.a.length = 0;
If you want to delete an element from a at a known index (let index)
list.a.splice(index, 1);
You're attempting to add the elements to the array object as object properties and not as array elements. You can verify this by inspecting the value of list.length (will be 0).
So when doing something such as the following:
function removeProperty(id) {
if (list.hasOwnProperty(id)) {
delete list[id];
}
}
removeProperty('a');
it's really the same as:
delete list.a;
which is why you think it leaves an undefined 'entry' in the 'list'.
You'll need to use a literal object instead:
var list = {};
list['a'] = [];
...
list['b' = [];
...
which would allow you to use delete and have it behave as you expect. Of course you'll lose the .length property on the array but you never had that anyway.
Create a simple prototype for the Array class
Array.prototype.remove = function() {
// Helper function to remove a single element from a list if exists
item = arguments[0]
if (this.includes(item)) {
index = this.indexOf(item)
this.splice(index, 1)
}
}
// Now we can call
myList.remove(YourObject)
The above code will add the remove method to all your lists, so this will help you not just for objects but also strings, integers, or any data type
var list = {1: [{},{}], 2: [{},{}]};
function removeProperty(obj, prop){
if(obj[prop]){
delete obj[prop];
}
}
removeProperty(list,"1");
console.log(list);
If this quote:
I would list to remove the entry based on the key (a/b)
means you would like to select the list to consider based off the key (a/b), then remove elements in the list (or all of them), you can try this:
var list = [];
list['a'] = [];
list['a'].push({obj: 'test4'});
list['a'].push({obj: 'test5'});
list['b'] = [];
list['b'].push({obj: 'test'});
list['b'].push({obj: 'test2'});
var toRemove = 'test4';
var removeFrom = "a";
var consideredList;
for (var prop in list) {
if (prop == removeFrom) {
consideredList = list[prop];
}
}
//Remove everything from the considered list
consideredList.splice(0, consideredList.length);
//Remove based off value, if you know the property name
// for(var pos in consideredList) {
// if(consideredList[pos].obj == toRemove) {
// consideredList.splice(pos, 1);
// }
// }
I made a Plunker of a few different cases (check the script.js file). There seems to be a bit of confusion on what you are after and hopefully this is helpful to you somehow. Good luck.