How to compare JSON object structure, compare [{},{},{}] vs {k:v, k2:v2,...} - javascript

In my PHP app, for 2 different tools I get 2 json responses but I would like to use the same javascript to handle both.
All I need to do is to compare this structure
data = [
{'k1':'v1'},
{'k2':'v2'},
{'k3':'v3'}
]
with this one:
data = {'k11':'v11', 'k22':'v22', 'k33':'v33'}
this must work for any number of results (they are not suppose to match) and it also needs to work if 1 result is given in any of both cases.
I've already tried
- using data.length (which I thought in the second case would give me 1 array
- data instanceof Array, which is true for both cases (same thing if I do data instanceof Object)
What is the best approach to compare these in javascript?
EDITED: the keys and values of both json are not suppose to match, I only want to compare the structure, or detect one of them without having the other. (array of objects vs object with properties)

The first one is an array of objects; the second is an object with properties.
If you need to do this on the PHP side, just do json_decode($data, true) on the second data structure (the second argument casts it as an array) before output. You may have to play around with it a little to get the structures to line up but it should be pretty straight-forward.
Otherwise, in js -
var data = [
{'k':'v'},
{'k2':'v2'},
{'k3':'v3'}
],
data2 = {'k':'v', 'k2':'v2', 'k3':'v3'},
data3 = [];
for (var x in data2) {
data3.push({x:data2[x]});
}

To check if the received data is in the first format or the second one you may just check if the data is an array or not.
The correct way to do it is as follows:
if( Object.prototype.toString.call( data ) === '[object Array]' )
{
// an array: parse data according to first format
}
else
{
// not an array: parse data according to second format
}

There are several way you can do this. I would flatten the array so that it matches the format of the object:
function Flatten(source) {
if (!source || !source.length || source.length === 0) {
return source;
}
var output = {};
for (var i = 0; i < source.length; i++) {
for (prop in source[i]) {
if (source[i].hasOwnProperty(prop)) {
output[prop] = source[i][prop];
}
}
}
return output;
}
Here's a fiddle

I'm not actually sure what you mean by "compare", but if you want a purely JavaScript solution to finding out if the structures are equivalent, you could use this.
Simple Shallow Solution
function numOwnProperties (obj) {
var i = 0;
for (var key in obj) {
if (obj.hasOwnProperty(key)) { i++; }
}
return i;
}
function areEquivalent (obj, arr) {
var length = arr.length;
if (numOwnProperties(obj) !== length) { return false; }
for (var i = 0; i < length; i++) {
var item = arr[i];
for (var key in item) {
if (item.hasOwnProperty(key) && item[key] !== obj[key]) {
return false;
}
}
}
return true;
};
Usage
data1 = [
{'k':'v'},
{'k2':'v2'},
{'k3':'v3'}
];
data2 = {
'k':'v',
'k2':'v2',
'k3':'v3'
};
console.log(areEquivalent(data2, data1)); // true

Related

How to convert a converted string back into an array?

As far as I know, you can only save strings to local storage. So, I had to write a function so that I could save arrays. If I call console.log(fixA(["string1", [5, [false]], "string2"])); I get an output of "'string1',[5,[false]],'string2'". Here it is:
function fixA(array) {
var toreturn = "";
for (var i = 0; i < array.length; i++) {
if (typeof array[i] === 'object') {
toreturn += "[" + fixA(array[i]) + "]";
} else {
if (typeof array[i] === 'string') {
toreturn += "'" + array[i] + "'";
} else {
toreturn += array[i];
}
}
if (i < array.length - 1) {
toreturn += ",";
}
}
return toreturn;
}
console.log(fixA(["string1", [5, [false]], "string2"]));
The issue now is that I have no clue how to convert it back. I've tried a few things but have always gotten stuck on how I convert the arrays back. This is basically what I've tried:
function fixS(string) {
var toreturn = [];
var temp = string.split(",");
for (var i = 0; i < temp.length; i++) {
// I could run a check here to see if temp[i] starts with "[", but I'm not sure how to tell where the array ends.
// If it is an array, then I'd need to pass everything inside of the array back into fixS, making it recursive.
// The times I tried to do those two things above, I ran into the issue that the commas inside of the sub arrays also split everything, which I don't want (as the recursive function will deal with that).
toreturn.push(temp[i]);
}
return toreturn;
}
console.log(fixS("'string1',[5,[false]],'string2'"));
// This also doesn't return numbers as numbers or booleans as booleans.
Not much there, but it's as far as I've gotten. Any help is appreciated.
Instead of doing your own bespoke solution, unless you have something that can't be represented in JSON (your example can be), use JSON:
On page load:
var data = JSON.parse(localStorage.getItem("data") || "null");
if (!data) {
// There wasn't any, initialize
}
or
var data = JSON.parse(localStorage.getItem("data") || "{}");
...if you want a blank object if there is nothing in local storage.
When saving your data:
localStorage.setItem("data", JSON.stringify(data));
As David said there's JSON.stringify() & JSON.parse();
you can use those methods :
function save_to_storage(id, anything){
localStorage.setItem(id, JSON.stringify(anything));
}
function load_from_storage(id){
return JSON.parse(localStorage.getItem(id));
}
It can be achieved with help of JSON.stringify and JSON.parse functions.
Let me explore with help of code snippet.
var original_arr = ["string1", [5, [false]], "string2"]; // array
// Converting this array into string
var converted_str = JSON.stringify(original_arr); // converted into string
// Converting this string back to array
var new_arr = JSON.parse(converted_str ); // back to array
The other answers have already covered it, but note that P5.js also provides functionality for working, saving, and loading JSON directly.
Take a look at the saveJSON() and loadJSON() functions in the reference.

How to group all values that have the same key into an array

First, sorry if you find the question confusing.
Basically, I have an object like this:
[{"6":6.5},{"4":4.2},{"6":6.3}]
What I want to do, is to remove the duplicated keys but keep there values and push them all into one unique key only, as an array. like this:
[{"6":[6.5, 6.3]}, {"4": 4.2}]
Can anyone suggest a solution?
.reduce() is what you want:
var data = [{"6":6.5},{"4":4.2},{"6":6.3}];
var res = data.reduce((rv, obj) => {
var key = Object.keys(obj)[0];
rv[key] = rv[key] || [];
rv[key].push(obj[key]);
return rv;
}, {});
console.log(res);
Note: This returns data always in the format of arrays (Even if there is one value). If you're looking for the exact format you specified, you just need to add more logic as I've demonstrated below (Although, I wouldn't recommend this approach, as it adds more complication down the line.)
var data = [{"6":6.5},{"4":4.2},{"6":6.3}];
var res = data.reduce((rv, obj) => {
var key = Object.keys(obj)[0];
if (Array.isArray(rv[key])) { // Already is an array
rv[key].push(obj[key]);
} else if (rv[key] !== undefined) { // Has a value. Convert to array
rv[key] = [rv[key], obj[key]];
} else { // Haven't seen this key yet. Set the value
rv[key] = obj[key];
}
return rv;
}, {});
console.log(res);

Get object out of multi dimensional array

I have three arrays filled with objects in JavaScript that I've put into a multi-dimensional array in order to get all possible combinations of all three objects to create a set of 'rules'. Then later in the code I want to be able to pull out the properties of these objects, but when I attempt to all I seem to be able to get is the string "[object]" and not the actual object itself.
This is how the array is put together:
var allArrays = [settings.serviceLevels, settings.serviceDays, settings.services];
function allPossibleCases(arr) {
if (arr.length === 1) {
return arr[0];
} else {
var result = [];
var allCasesOfRest = allPossibleCases(arr.slice(1));
for (var i = 0; i < allCasesOfRest.length; i++) {
for (var j = 0; j < arr[0].length; j++) {
result.push(arr[0][j] + allCasesOfRest[i]);
}
}
return result;
}
}
var uncheckedRules = allPossibleCases(allArrays);
I need to be able to get the properties out of settings.serviceLevels, settings.serviceDays and settings.services - I can find questions that do this with regular arrays but not with objects - is this even possible or have I taken it to a level where I've lost the properties?
Not sure I understood what you wanted. But here's a recursive function that will store in an array the properties of objects contained in another array :
function listProps(object, result) {
// for each property of the object :
for (objectProperty in object) {
// if it's an object, recursive call :
if (typeof object[objectProperty] === 'object') {
listProps(object[objectProperty], result);
} else { // otherwise, push it into the result array :
result.push(object[objectProperty]);
}
}
}
var allArrays = [settings.serviceLevels, settings.serviceDays, settings.services];
var result = [];
listProps(allArrays, result);
The result array should list every properties from the three objects settings.serviceLevels, settings.serviceDays, settings.services and their children (if they contain any object themselves).

Accessing a value in an object of unknown name that is stored in an array (JavaScript)

Its hard to give this question a good name. I'll jump right into the example:
var people = [
{
john: {
score: 1
}
},
{
adam: {
score: 2
}
}
];
I need to loop through this array of objects and get the score values, the trick is I don't know the names (john, adam, ...) but I know each contains a score value.
How do I get them? My current code looks like this:
var pplArr = [];
for (i=0; i<people.length; i++) {
for (var key in people[i]) {
pplArr.push(key);
}
}
for (j=0; j<pplArr.length; j++) {
console.log(pplArr[j]);
//var nameVar = eval(pplArr[j]);
//console.log(people.[j].nameVar.score)
}
The commented section doesn't work since eval() returns undefined otherwise I think it would work. I also tried for (var key in people) and its variations with limited success. Also my two loops look like they are over-complicated, is there no simpler way?
I'm using AngularJS (ionic) thus I would prefer not to use jQuery.
You can use map() and Object.keys().map() will help to iterate over array and Object.keys() helps to gets object keys as an array, get first one from array and get the value.
var people = [{
john: {
score: 1
}
}, {
adam: {
score: 2
}
}];
var res = people.map(function(v) {
return v[Object.keys(v)[0]].score;
});
console.log(res);
Since you don't know if you have more than one person in each object you can recourse in the object looking for all properties named score and return the values in an array like this:
function getAllProperties(o, p){
var properties = [];
Object.keys(o).forEach(function (k) {
if (typeof o[k] === "object") {
properties = properties.concat(getAllProperties(o[k], p));
} else if (k === p) {
properties.push(o[k]);
}
});
return properties;
}
console.log(getAllProperties(people, "score"));
If you want the score of each object in people array, you could do something like this:
for (var i = 0; i < people.length; i++) {
var person = people[i];
for (var key in person) {
for (score in person[key]) {
console.log(person[key][score])
}
}
}

Search 2d javascript array for a known complete array

I have a 2d array similar to this:
var array = [
{100,200},
{200,200},
{100,400}
];
Now I want to find if a known array exists inside the 2d array. For example, I want to check if [200,200] exists as a 2nd level array inside the 2d array.
On 1d arrays in the past I've used something like:
if (value in array) {...}
Can't seem to get that method working on 2d. What is the best solution?
Not sure if you already know but your syntax is not correct. It should look like this:
var array = [
[100,200],
[200,200],
[100,400]
];
A naive way to check if [200, 200] exists as a 2nd level array:
console.log(array[1][0] == 200 && array[1][1] == 200);
Another naive approach is to use a nested loop and go through each item.
If you want a fast approach to this, you might want to read up on search algorithms.
Searching Algorithms
var array = [
[100,200],
[200,200],
[100,400]
];
var searchFor = [200,200];
function arrayExistsInside(haystack, needle) {
for(var i = 0; i < haystack.length; i++) {
if(compareArray(haystack[i], needle)) return true;
}
return false;
}
function compareArray(array1, array2) {
if(array1.length != array2.length) return false;
for(var i = 0; i < array1.length; i++) {
if(array1[i] != array2[i]) return false;
}
return true;
}
if(arrayExistsInside(array, searchFor)) { ... }
You could also use the compare function outlined on How to compare arrays in JavaScript?

Categories