Grab Keys from JSON Array Without Repeating - javascript

Consider the following JSON array returned from a request sent to an Elasticsearch cluster:
[
{
"_type": "Event example",
"_source": {
"democarrier_s": "vodafone UK",
"m-Ecosystem_s": "iOS",
"demo-application": "demo",
"demo-country-code": "GB"
}
},
{
"_type": "Event example",
"_source": {
"democarrier_s": "Verizon",
"m-Ecosystem_s": "iOS",
"demo-application": "demo1",
"demo-country-code": "US"
}
}
]
I am trying to figure out how to grab all of the unique keys from this document without repeating and without hard-coding any of the values and store them into an object of the following form:
columns = ['_type', '_source.democarrier_s', '_source.m-Ecosystem_s', '_source.demo-application', '_source.demo-country-code'];
Could someone help me figure out how to achieve this? I've been trying to loop through the document and store the keys but I can't quite figure it out. '_type' can be hard coded into the columns object because it will always exist.
Thank you in advance for your help and time.

If it's just 2 levels you could do this:
var data = [
{
"_type": "Event example",
"_source": {
"democarrier_s": "vodafone UK",
"m-Ecosystem_s": "iOS",
"demo-application": "demo",
"demo-country-code": "GB"
}
},
{
"_type": "Event example",
"_source": {
"democarrier_s": "Verizon",
"m-Ecosystem_s": "iOS",
"demo-application": "demo1",
"demo-country-code": "US"
}
}
]
var keys = [];
data.forEach(function(item) {
for (var key in item) {
var hasProperties = false;
if (typeof item[key] !== 'string') {
for (var key2 in item[key]) {
hasProperties = true;
var keyName = key + "." + key2;
if (keys.indexOf(keyName) < 0)
keys.push(keyName);
}
}
if (!hasProperties && keys.indexOf(key) < 0) {
keys.push(key);
}
}
});
keys.forEach(function (k) { console.log(k) });

Related

How to add a new key to multiple indices of an array of objects?

I've got an array of three people. I want to add a new key to multiple objects at once based on an array of indices. Clearly my attempt at using multiple indices doesn't work but I can't seem to find the correct approach.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
}
];
array[0,1].title = "Manager";
array[2].title = "Staff";
console.log(array);
Which returns this:
[
{
"name": "Tom",
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
But I'd like it to return this.
[
{
"name": "Tom",
"title": "Manager"
},
{
"name": "Dick",
"title": "Manager"
},
{
"name": "Harry",
"title": "Staff"
}
]
You cannot use multiple keys by using any separator in arrays.
Wrong: array[x, y]
Correct: array[x] and array[y]
In your case, it will be array[0].title = array[1].title = "manager";
1st method::
array[0].title = "Manager";
array[1].title = "Manager";
array[2].title = "Staff";
array[0,1] will not work.
2nd method::
for(var i=0;i<array.length;i++) {
var msg = "Manager";
if(i===2) {
msg = "Staff"
}
array[i].title = msg
}
You can use a helper function like this
function setMultiple(array, key, indexes, value)
{
for(i in array.length)
{
if(indexes.indexOf(i)>=0){
array[i][key] = value;
}
}
}
And then
setMultiple(array, "title", [0,1], "Manager");
Try this: `
for (var i=0; var<= array.length; i++){
array[i].title = "manager";
}`
Or you can change it around so var is less than or equal to any n range of keys in the index.
EDIT: instead make var <= 1. The point is to make for loops for the range of indices you want to change the title to.
Assuming that you have a bigger set of array objects.
var array = [
{
"name": "Tom",
},
{
"name": "Dick",
},
{
"name": "Harry",
},
.
.
.
];
Create an object for the new keys you want to add like so:
let newKeys = {
'Manager': [0,2],
'Staff': [1]
}
Now you can add more such titles here with the required indexes.
with that, you can do something like:
function addCustomProperty(array, newKeys, newProp) {
for (let key in newKeys) {
array.forEach((el, index) => {
if (key.indexOf(index) > -1) { // if the array corresponding to
el[newProp] = key // the key has the current array object
} // index, then add the key to the
}) // object.
}
return array
}
let someVar = addCustomProperty(array, newKeys, 'title')

Remove an object from array

am trying to remove an object from an Array list within a JavaScript object.
The Structure if the Object:
{
"temp": {
"name": "",
"css": {
"bg_color_main": "#xxxxx",
"part_bg_color": "xxxxx",
"txt_font_family": "xxxxxxxx",
"txt_font_color_main": "#xxxxx",
"headline_font_family": "xxxxx",
},
"part": [
{
"name": "xxxxxx",
"style": {}
},
{
"name": "yyyyyy",
"style": {}
},
{
"name": "zzzzzz",
"style": {}
}
]
}
}
The Code:
$.each(jsonData.temp.part, function(k, v) {
var tt = this; //var tt = $(this)
if( v.name === partName ){
delete tt[k];
}
});
Nothing happens.. no error, no warning!
There are two problems in your code. First, delete does not remove elements. It only sets them to undefined. Use splice instead.
Second, it never gets to do that, because tt (or this) is the object inside the array that you are currently working on, not the array you are iterating. You need to access the array explicitly with its full name.
$.each(jsonData.temp.part, function(k, v) {
var tt = this; //var tt = $(this)
if( v.name === partName ){
jsonData.temp.part.splice(k,1);
}
});
Alternatively you could simply use a filter.
var o = {
"temp": {
"name": "",
"css": {
"bg_color_main": "#xxxxx",
"part_bg_color": "xxxxx",
"txt_font_family": "xxxxxxxx",
"txt_font_color_main": "#xxxxx",
"headline_font_family": "xxxxx",
},
"part": [
{
"name": "xxxxxx",
"style": {}
},
{
"name": "yyyyyy",
"style": {}
},
{
"name": "zzzzzz",
"style": {}
}
]
}
}
o.temp.part = o.temp.part.filter(function (element) {return element.name !== "zzzzzz"});
You could use different approach, for example:
If the reference of the array is not needed, you can use reduce to create a new array:
jsonData.temp.part = jsonData.temp.part.reduce(function(acc, value) {
if( value.name !== partName ){
acc.push(value);
}
return acc;
}, []);
Also you can find the index of the element, and use splice to mantain the reference:
var indexElement = jsonData.temp.part.reduce(function(acc, value, index) {
if( value.name !== partName ){
return index;
}
return acc;
}, -1);
jsonData.temp.part.splice(indexElement, 1)
Both ways work.
Here is a possible solution:
The simplest way is to use delete.
var jsonData = {
"temp": {
"name": "",
"css": {
"bg_color_main": "#xxxxx",
"part_bg_color": "xxxxx",
"txt_font_family": "xxxxxxxx",
"txt_font_color_main": "#xxxxx",
"headline_font_family": "xxxxx",
},
"part": [
{
"name": "xxxxxx",
"style": {}
},
{
"name": "yyyyyy",
"style": {}
},
{
"name": "zzzzzz",
"style": {}
}
]
}
}
var nameToRemove = 'xxxxxx';
var parts = jsonData.temp.part;
$.each(parts, function(k, v) {
if (v.name === nameToRemove)
{
delete parts[k];
}
});
//this code is added to just show the result
$.each(parts, function(i, r){
if (r != undefined)
{
$('#result').append(r.name + ',')
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<label id="result"></label>
You created a copy and delete item from the copy.
$.each(jsonData.temp.part, function(k, v) {
var tt = this; // now you created a new array!!!
if( v.name === partName ){
delete tt[k]; // here you delete the item from the copy array
delete this[k]; // you remove item from the original array
}
});

Loop through object and replace values

I have an object which at some points is four levels deep, however I want a function that will cope should more levels be introduced. I'm trying to write a function that will replaced elements such that <span class="ajax-parent1-parent2-parent3-value"></span> will be replaced with parent1.parent2.parent3.value.
The issue is that the depth is variable, so I could have something like <span class="ajax-parent1-value"></span> to be replaced with parent1.value.
Finally, it's not always the text to be replaced. Optionally, data-attr can specify an attribute to be used instead (through element.attr(<data-attr>, <value>)).
Currently, I'm iterating manually, however it isn't very clean so I was wondering if there is a better way to do it. This also doesn't work for greater than two levels deep.
function render(data) {
$.each(data, function(parent, value) {
$.each(value, function(element, value) {
$el = $('.ajax-' + parent + '-' + element);
$.each($el, function(key, el) {
if ($(el).data('attr')) {
$(el).attr($(el).data('attr'), value);
} else {
$(el).text(value);
}
}
});
});
}
Example object:
{
"profile": {
"username": "johnd",
"bio": "Some example data about John",
"website": "http://john.com",
"profile_picture": "http://john.com/me.jpg",
"full_name": "John Doe",
"counts": {
"media": 13,
"followed_by": 26,
"follows": 49
},
"id": "16"
},
"dashboard": {
"script": {
"tags": ["media"],
"stats": {
"liked": 0,
"lastrun": "never",
"duration": 0
},
"status": {
"code": 0,
"slug": "disabled",
"human": "Disabled",
"message": "Not running."
}
},
"account": {
"plan": "free",
"created": 1419261005373,
"updated": 1419261005373
}
},
"serverInformation": {
"serverName": "Johns API",
"apiVersion": "0.0.1",
"requestDuration": 22,
"currentTime": 1419262805646
},
"requesterInformation": {
"id": "redacted",
"fingerprint": "redacted",
"remoteIP": "redacted",
"receivedParams": {
"action": "getDashboard",
"apiVersion": 1
}
}
}
Here is the solution I wrote:
function iterate(obj, stack) {
for (var property in obj) {
if (obj.hasOwnProperty(property)) {
if (typeof obj[property] == "object") {
iterate(obj[property], stack + '-' + property);
} else {
$group = $('.ajax' + stack + '-' + property);
$.each($group, function(key, element) {
if ($(element).data('attr')) {
$(element).attr($(element).data('attr'), obj[property]);
} else {
$(element).text(obj[property]);
}
});
}
}
}
}
Why don't you start from the HTML, so you only access the properties you actually want to render?
That way you can keep it quite simple (also note that this removes the need to nest HTML spans in the same order/depth as the data object, you can just place any HTML node anywhere. Just make sure you don't use class/node names more then once.
function parseData(data) {
var $container = $('.ajax');
$container.find("[class^='ajax-']").each(function(i, el) {
var $el = $(el);
if ($el.children().length === 0)
{
var nodes = $el.attr('class').split('-');
nodes.shift();
var node = data;
for (var i = 0; i < nodes.length; i++) {
node = node[nodes[i]];
if (typeof(node) == "undefined") {
break;
}
}
if ($el.data('attr'))
{
$el.attr($el.data('attr'), node);
}
else
{
$el.text(node);
}
}
});
}
Fiddle here:
http://jsfiddle.net/ckcduLhn/5/

JSON tree to parent-link structure

I have a JSON tree structure:
nodes =
[
{
"name": "user1",
"children": [
{
"name": "user2"
},
{
"name": "user3",
"children": [
{
"name": "user4"
}
]
},
{
"name": "user5"
}
]
}
]
that I would like to convert to a parent-link structure:
[{"name": "user1","parent": "null"},
{"name": "user2","parent": "user1"},
{"name": "user3","parent": "user1"},
{"name": "user4","parent": "user3"},
{"name": "user5","parent": "user1"}]
I have tried to traverse the tree recursively but without success accessing the parent object:
rebuild(nodes,parentLink);
function parentlink(key,value) {
var obj = { name: value , parent: ??? };
if (key == "name"){
nodes.push(obj);
}
}
function rebuild(o,func) {
for (i in o) {
func.apply(this,[i,o[i]])
if (typeof(o[i])=="object") {
traverse(o[i],func,nodes);
}
}
}
In developer tools I can see the parent objects for each child, but I don't know how to access them. What should I do to add the parent to each user?
I'm not gonna lie, I didn't bother looking at your code - this is how I would do it:
http://jsfiddle.net/J6G2W/1/
function processChildren(item, ret, parent) {
for (var i = 0; i < item.length; i++) {
var cur = item[i];
var cur_name = cur.name;
ret.push({"user": cur_name, "parent": parent});
if ("children" in cur && cur.children.length > 0) {
processChildren(cur.children, ret, cur_name);
}
}
}
var all = [];
processChildren(nodes, all, null);
console.log(JSON.stringify(all));
Output is:
[{"user":"user1","parent":null},{"user":"user2","parent":"user1"},{"user":"user3","parent":"user1"},{"user":"user4","parent":"user3"},{"user":"user5","parent":"user1"}]
Which seems to be what you're looking for. You're welcome to modify what of my code to work more like yours, I just thought I'd share what I'd do :)
UPDATE:
If, for some reason, you want to make it more extendable, you can customize which keys are the "name" and which are the "children"...for example:
http://jsfiddle.net/J6G2W/2/
function startProcess(item, ret, key_look, children_look, parent) {
function processChildren(item2, ret2, parent2) {
for (var i = 0; i < item2.length; i++) {
var cur = item2[i];
var cur_name = key_look in cur ? cur[key_look] : null;
ret.push({"user": cur_name, "parent": parent2});
if (children_look in cur && cur[children_look].length > 0) {
processChildren(cur[children_look], ret, cur_name);
}
}
}
processChildren(item, ret, parent);
}
var all = [];
startProcess(nodes, all, "name", "children", null);
console.log(JSON.stringify(all));
Notice how you only have to specify the key_look, children_look arguments once. The inner function can access those parameters while only passing the important things each recursion. This probably isn't important, I just wanted to figure it out :)

searching a nested javascript object, getting an array of ancestors

I have a nested array like this:
array = [
{
"id": "67",
"sub": [
{
"id": "663",
},
{
"id": "435",
}
]
},
{
"id": "546",
"sub": [
{
"id": "23",
"sub": [
{
"id": "4",
}
]
},
{
"id": "71"
}
]
}
]
I need to find 1 nested object by its id and get all its parents, producing an array of ids.
find.array("71")
=> ["546", "71"]
find.array("4")
=> ["546", "23", "4"]
What's the cleanest way to do this? Thanks.
Recursively:
function find(array, id) {
if (typeof array != 'undefined') {
for (var i = 0; i < array.length; i++) {
if (array[i].id == id) return [id];
var a = find(array[i].sub, id);
if (a != null) {
a.unshift(array[i].id);
return a;
}
}
}
return null;
}
Usage:
var result = find(array, 4);
Demo: http://jsfiddle.net/Guffa/VBJqf/
Perhaps this - jsonselect.org.
EDIT: I've just had a play with JSONSelect and I don't think it's appropriate for your needs, as JSON does not have an intrinsic 'parent' property like xml.
It can find the object with the matching id, but you can't navigate upwards from that. E.g.
JSONSelect.match(':has(:root > .id:val("4"))', array)
returns me:
[Object { id="4"}]
which is good, it's just that I can't go anywhere from there!

Categories