JS: Finding next key in json - javascript

I have the following json:
{0: "2", 1: "2", $$hashKey: "object:35", undefined: "1"}
Currently I am trying to get its key-value with the below code:
var data = JSON.stringify(row);
var result = $.parseJSON(data);
$.each(result, function (k, v) {
//display the key and value pair
console.log(k, v);
});
The above code works fine and I can get my key-value from it.
Now what I am trying to get is the next key-value pairs within the $.each loop.
For example if in the loop the current key is "0" I want to get the next key "1" in the same call itself. If in the loop the current key is "1" I want to get the next key "$$hashKey" along with their values.
Is it possible to do so? I am open to code changes above if required.

You can use Object.keys to get the keys to an array, then run through it with a forEach to have access to the keys index. Important to note that objects are unordered, so your key order one time may differ from the next time:
var keys = Object.keys(obj);
keys.forEach(function(key, index) {
var nextIndex = index + 1;
if (nextIndex === keys.length) return; //out of bounds
var nextKey = keys[nextIndex];
});
Edit: As pointed out by the comments - if you want the keys in the same order each time, call .sort() on your keys array with your desired sort logic.

Understanding now that the goal is to retrieve keys in the order they appear in JSON, a couple of thoughts:
(1) if you control the source of the object ("row" in the OP code), don't represent it as an object. instead use an array of key-value pairs: [[0, "2"], [1, "2"], [$$hashKey, "object:35"], [undefined, "1"]].
otherwise, (2) roll your own JSON parser that returns an array of key-value pairs for an object. This post looks to be a sensible start. Or, you can vastly simplify the task if you are able to make certain assumptions about the values, for example, say you know that all values are strings...
// parse a string representing an object, returning an array of key-value pairs.
// assumes values are strings that do not contain commas or colons
function myProbablyNegligentlySimpleJSONParse(string) {
let trimmed = string.trim().slice(1, -1);
let components = trimmed.split(',');
return components.map(kvString => {
let kv = kvString.split(':');
return [ kv[0].trim(), kv[1].trim() ];
});
}
forEach passes the current index to the iterator function, so that int can be used to look ahead or behind in the iteration.
var data = '{0: "2", 1: "2", $$hashKey: "object:35", undefined: "1"}';
let result = myProbablyNegligentlySimpleJSONParse(data);
result.forEach(function (pair, index) {
let [k, v] = pair; // now k and v are your key and value
console.log(`key is ${k} value is ${v}`)
if (index < result.length-1) {
let [nextK, nextV] = result[index+1];
console.log(`next key is ${nextK} next value is ${nextV}`);
}
});

You could turn your object into an iterable and which will return the next [key, value] pair each time you call next on the iterator:
function makeIterable(o) {
o[Symbol.iterator] = () => {
var keys = Object.keys(o);
var i = 0;
return {
next() {
var done = false;
var value = [keys[i + 1], o[keys[i + 1]]];
if (i >= (keys.length - 1)) {
done = true;
}
i++;
return {
value,
done
}
}
};
}
}
var jsonStr = '{ "0": "2", "1": "2", "$$hashKey": "object:35", "undefined": "1" }';
var obj = JSON.parse(jsonStr);
makeIterable(obj);
var itr = obj[Symbol.iterator]();
while (true) {
var item = itr.next();
if (item.done) {
break;
}
console.log(item.value);
}

Related

Generate a query string from a jQuery array with multiple values for a query var

I wrote the following code which takes an array and converts it into a filter string, the string gets generated but instead of
product_size=123&product_size=456
I want this to be product_size=123+456.
I suspect I need to check if the array key for product_size already exists and then push the second number into it, then generate the filter string.
I have created a keysAlreadyUsed array but I can't figure out how to do this.
Maybe I am over thinking this and some form of string manipulation would suffice.
// Array
arrayTest = [];
arrayTest.push( ['product_size', 123] );
arrayTest.push( ['product_size', 456] );
arrayTest.push( ['product_color', 123] );
// Start filter string and array of keys already used
filterString = '';
keysAlreadyUsed = [];
// Loop the array test
$.each( arrayTest, function( index1, value1 ) {
// If the key has already been set
if( jQuery.inArray( value1[0], keysAlreadyUsed ) ) {
// Push into the existing array key
} else {
// New array we can loop through later with the product_size and the 123, 456 in it.
}
// Populate filter string
filterString += value1[0] + '=' + value1[1] + '&';
// Push the key already used into the keys already used array
keysAlreadyUsed.push( value1[0] );
});
// Output
console.log(filterString);
console.log(keysAlreadyUsed);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
jsFiddle: https://jsfiddle.net/6oxc8umz/2/
Maybe is not the most efficient.
The first call of function reduce for creating a key-value object and the second call for generating the queryString.
let arrayTest = [['product_size', 123], ['product_size', 456] ,['product_color', 123]],
result = Object.entries(arrayTest.reduce((a, [key, value]) => {
if (a[key]) a[key] += `+${String(value)}`;
else a[key] = value;
return a;
}, Object.create(null))).reduce((a, [key, value]) => a + `${key}=${value}&`, "").slice(0, -1);
console.log(result);
First I'd turn the array into an object so you can associate the key to the concatenated values. Because you're using jQuery, you can utilize $.param to format it as you need to.
const arrayTest = [['product_size', 123],['product_size', 456],['product_color', 123]];
const toFilterString = obj => decodeURIComponent($.param(obj));
let params = arrayTest.reduce((output, [key,value]) => {
if (output[key]) output[key] += `+${value}`; //if key exists, concatenate value
else (output[key]) = value; //else, set value
return output;
}, {});
console.log(toFilterString(params));
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
If you need the value to be URL-encoded, you can remove decodeURIComponent(...).

How to dynamically change the key names of object properties in an array

I have an array of objects, like so:
arr = [{"timeslot":"6am7am","AVG(Monday)":10,"AVG(Tuesday)":11,"AVG(Wednesday)":7}]
Each object will always contain the "timeslot" property, and can contain any combination of the day-of-the-week properties, Monday through Sunday. Each day of the week may only be represented once in a single object.
I want to alter each object: specifically, the key names of the day-of-the-week properties only (the "timeslot" property will be unchanged"), to get an array like so:
newArr = [{"timeslot":"6am7am","Monday":10,"Tuesday":11,"Wednesday":7}]
My slightly unreadable solution works:
// Iterate the array of objects
results.forEach(function(o) {
// Iterate the object's properties
Object.keys(o).forEach(function(k) {
if(k.includes("AVG")) {
var len = k.length;
var pos = len - 1;
var newKey = k.slice(4, pos); // Extract the day of the week from the key name
o[newKey] = o[k]; // Create the new property with the same value and the new key-name
delete o[k]; // Delete the original property
}
});
});
How can I improve this solution?
Instead of mutating the original array by adding and removing keys from each object, Array#map the array into a new array, and recreate the objects using Array#reduce:
var arr = [{"timeslot":"6am7am","AVG(Monday)":10,"AVG(Tuesday)":11,"AVG(Wednesday)":7}];
var result = arr.map(function(obj) {
return Object.keys(obj).reduce(function(r, key) {
var k = key.includes('AVG') ? key.slice(4, -1) : key;
r[k] = obj[key];
return r;
}, {});
});
console.log(result);

Unable to add key-value pair in Javascript Object after sorting them

Hey I have an object as
data = { vaibhavs:243 "132" ,
vaibhavs:918 "342",
karthikp:265 "345",
marimuthu:848 "asjkd",
ushas:918 "sdf15",
apoorvg:728 "wddfs"....
}
I need to sort it on the basis of key ,i.e., "vaibhavs:243" alphabatically.
I have written the following code for it. I am able to sort the keys, but not able to put it back in the returning object. Please guide what to do.
function sortData(data) {
var sorted = [];
Object.keys(data).sort(function (a, b) {
return a < b ? -1 : 1
}).forEach(function (key) {
var obj = {};
obj[key] = data[key]
sorted.push(obj);
});
return sorted;
}
This is giving me an array of object which i don't want. I want a single object.
I can't do this using an array because I need the sorted key-value pairs.
If I am using the following code,
function sortData(data) {
var sorted = {};
Object.keys(data).sort(function (a, b) {
return a < b ? -1 : 1
}).forEach(function (key) {
sorted[key] = key[data]
});
return sorted;
}
The above code is always overwriting the existing value. Can someone suggest me how to add the Key value pair. As I have that in sorted order, but just unable to add them in a single object.

Find duplicate object values in an array and merge them - JAVASCRIPT

I have an array of objects which contain certain duplicate properties: Following is the array sample:
var jsonData = [{x:12, machine1: 7}, {x:15, machine2:7},{x:12, machine2: 8}];
So what i need is to merge the objects with same values of x like the following array:
var jsonData = [{x:12, machine1:7, machine2:8}, {x:15, machine2:7}]
I like the lodash library.
https://lodash.com/docs#groupBy
_.groupBy(jsonData, 'x') produces:
12: [ {x=12, machine1=7}, {x=12, machine2=8} ],
15: [ {x=15, machine2=7} ]
your desired result is achieved like this:
var jsonData = [{x:12, machine1: 7}, {x:15, machine2:7},{x:12, machine2: 8}];
var groupedByX = _.groupBy(jsonData, 'x');
var result = [];
_.forEach(groupedByX, function(value, key){
var obj = {};
for(var i=0; i<value.length; i++) {
_.defaults(obj, value[i]);
}
result.push(obj);
});
I'm not sure if you're looking for pure JavaScript, but if you are, here's one solution. It's a bit heavy on nesting, but it gets the job done.
// Loop through all objects in the array
for (var i = 0; i < jsonData.length; i++) {
// Loop through all of the objects beyond i
// Don't increment automatically; we will do this later
for (var j = i+1; j < jsonData.length; ) {
// Check if our x values are a match
if (jsonData[i].x == jsonData[j].x) {
// Loop through all of the keys in our matching object
for (var key in jsonData[j]) {
// Ensure the key actually belongs to the object
// This is to avoid any prototype inheritance problems
if (jsonData[j].hasOwnProperty(key)) {
// Copy over the values to the first object
// Note this will overwrite any values if the key already exists!
jsonData[i][key] = jsonData[j][key];
}
}
// After copying the matching object, delete it from the array
// By deleting this object, the "next" object in the array moves back one
// Therefore it will be what j is prior to being incremented
// This is why we don't automatically increment
jsonData.splice(j, 1);
} else {
// If there's no match, increment to the next object to check
j++;
}
}
}
Note there is no defensive code in this sample; you probably want to add a few checks to make sure the data you have is formatted correctly before passing it along.
Also keep in mind that you might have to decide how to handle instances where two keys overlap but do not match (e.g. two objects both having machine1, but one with the value of 5 and the other with the value of 9). As is, whatever object comes later in the array will take precedence.
const mergeUnique = (list, $M = new Map(), id) => {
list.map(e => $M.has(e[id]) ? $M.set(e[id], { ...e, ...$M.get(e[id]) }) : $M.set(e[id], e));
return Array.from($M.values());
};
id would be x in your case
i created a jsperf with email as identifier: https://jsperf.com/mergeobjectswithmap/
it's a lot faster :)

javascript: How to get highest layer of json object as array?

With an array of objects in a form like this:
[
{
1429={
{
8766={...},
8483={...},
7345={...}
}
}
},
{
9041={...}
}
]
how could i get back an array like this?:
[1429, 9041]
If the array of objects would be in another structure this code would work:
var obj = {
"5": "some",
"8": "thing"
};
var keys = $.map(obj, function (value, key) {
return key;
});
console.log(keys);
That would return [5, 8]. But in my example it just would return the indexes [0,1]
Even if I wouldn't know the depth of the object - is it possible to get the values on that level? I dont need the indexes, I need those values. I couldn't find anything about it so far. Any tips for me maybe?
P.S.: I know that i could work out something with these keys and a loop, but I'm just asking for a simplier way to do it.
Regards
you are looking for the keys in a json object, you can get them this way:
Object.keys(obj);
for the object example:
var obj = {
"5": "some",
"8": "thing"
};
you will get:
["5","8"]
for an array of object of this type:
var arrayObject = [{},{},{}];
you can use a map and get the keys:
var keys = arrayObject.map(function(k){
return Object.keys(k);
});
keys is an array of arrays of keys. Example, for the following object (similar to your data structure):
var l= [
{
1429:{
8766: "test",
8483:"test",
7345: "test"
}
},
{
9041: "test"
}
];
you will get:
[["1429"],["9041"]]
apply concat and you will get what you are looking for. Here how to apply concat in the case of multiple arrays.
var arrayOfKeys = [].concat.apply([], keys);
now you will get:
["1429","9041"];
In your specific case you could use
var keys = [];
root.forEach(function(v) { keys = keys.concat(Object.keys(v)); });
If instead you have a tree of arrays and you want the keys of all other objects instead (but not recursing into objects) then a simple recursive function would do it:
function topKeys(x) {
if (x && x.constructor === Array) {
var result = [];
x.forEach(function(item) {
result = result.concat(topKeys(item));
});
return result;
} else if (typeof x === "object") {
return Object.keys(x);
} else {
return [];
}
}

Categories