I am using Nodejs. I need to store a JavaScript object in a relational database. Each key in the JavaScript object represents column name in DB. I have following:
var data = {
"aa": "99",
"bb": ["11","22"],
"cc": ["44","55","66"]
}
I want to convert this object into an array as follow:
data = [
{
"aa": "99",
"bb": "11",
"cc": "44"
},
{
"aa": "99",
"bb": "11",
"cc": "55"
},
{
"aa": "99",
"bb": "11",
"cc": "66"
},
{
"aa": "99",
"bb": "22",
"cc": "44"
},
{
"aa": "99",
"bb": "22",
"cc": "55"
},
{
"aa": "99",
"bb": "22",
"cc": "66"
}
]
Is there any way to do this ? I guess using recursive we can do it. But could not find any reference in Google.
You could use an iterative and recursive approach with a combination algorithm.
This solution dies basically iterate through the given data (an array is made out of the object) and inside of the array iterated over the items. In this case, you have an array with three arrays with the items.
[
["99"],
["11","22"],
["44","55","66"]
]
It starts with the first array and iterates. Here is only one item and the callback of the iteraton check for the part length and if it is equal to the given array length, al item are acually found. This is the cndition to exit the iteration adn to push the collected parts to the result array. (The items are converted to an object in the reduce callback.)
If the part array does not have the needed length, proceed with the next item of the outer array.
Basically the iteration and recusion works as follow
part 0 1 2 action
------ ----- ----- ---------------
99 go to next level
99 11 go to next level
99 11 44 push to result, end level 2
99 11 55 push to result, end level 2
99 11 66 push to result, end level 2
99 11 end level 1
99 22 go to next level
99 22 44 push to result, end level 2
99 22 55 push to result, end level 2
99 22 66 push to result, end level 2
99 22 end level 1
99 end level 0
function combine(object) {
function c(part) {
array[part.length].forEach(function (a) {
var p = part.concat(a);
if (p.length === array.length) {
result.push(p.reduce(function (r, b, i) {
r[keys[i]] = b;
return r;
}, {}));
return;
}
c(p);
});
}
var keys = Object.keys(object),
array = keys.map(function (k) { return Array.isArray(object[k]) ? object[k] : [object[k]]; }),
result = [];
c([]);
return result;
}
var data = { aa: "99", bb: ["11", "22"], cc: ["44", "55", "66"] },
result = combine(data);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
If it's ok to use ES6, a generator could be used to iterate through all layers (not necessarily better and not the most readable, but I just like iterators :) )
function flatten(data) {
const getArr = a => Array.isArray(a) ? a: [a],
keyData = Object.keys(data).map(k=>({name:k,arr:getArr(data[k])})),
getobj = function*(i) {
for(let p of keyData[i].arr)
for(let o of i===keyData.length -1 ? [{}] : getobj(i+1)){
o[keyData[i].name] = p;
yield o;
}
}
return [...getobj(0)];
}
let data = { aa: "99", bb: ["11", "22"], cc: ["44", "55", "66"] };
console.log(flatten(data));
Related
I have been searching the web for this, and can only seem to locate the Object.keys(obj).length which counts the length of the JSON. I am looking to loop through the JSON and count the values which are the same. In an Array of JSON objects.
[
{
"name": "Bob",
"age": 36
},
{
"name": "Billy",
"age": 18
},
{
"name": "Bob",
"age": 45
}
]
For example, I want to count many Bob there are. I would like to then output to a component like so.
There were 2 Bob
How can this be achieved? In certain situations I will know that there could only be say 4 values for example. SITE, MOBILE, CTV, VIDEO etc, I forsee collecting them and counting them would be easier than counting a random result.
const input = [
{
"name": "Bob",
"age": 36
},
{
"name": "Billy",
"age": 18
},
{
"name": "Bob",
"age": 45
}
]
let sums = input.reduce((a, r) => {
a[r.name] ??= 0
a[r.name] ++
return a
}, {})
console.log(sums)
Now use sums variable to print data in any form you like.
I just can't understand this concept in the function below:
acc[line[0]]
I really can't get my head around that piece of code, how come it is not be an error and works perfectly? How do you interpret it in English words? In my head it is an empty object acc {} at its first iteration and according to the piece of code is trying to access the iterated line at its first value [0]. How come it works without the need of inverted commas as well? And how does line[0] ended up to be the values of the object?
Here is the full code:
let output = [["mark johansson", "waffle iron", "80", "2"],
["mark johansson", "blender", "200", "1"],
["mark johansson", "knife", "10", "4"],
["Nikita Smith", "waffle iron", "80", "1"],
["Nikita Smith", "knife", "10", "2"],
["Nikita Smith", "pot", "20", "3"]]
let result =output.reduce((acc,line)=>{
acc[line[0]] = acc[line[0]] || []
acc[line[0]].push({
name: line[1],
price: line[2],
quant: line[3]
})
return acc
},{})
console.log(JSON.stringify(result,null,1))
{
"mark johansson": [
{
"name": "waffle iron",
"price": "80",
"quant": "2"
},
{
"name": "blender",
"price": "200",
"quant": "1"
},
{
"name": "knife",
"price": "10",
"quant": "4"
}
],
"Nikita Smith": [
{
"name": "waffle iron",
"price": "80",
"quant": "1"
},
{
"name": "knife",
"price": "10",
"quant": "2"
},
{
"name": "pot",
"price": "20",
"quant": "3"
}
]
}
Maybe if we replace all the dynamic references with hard-coded values from the first array - or line - in output, it will be clearer as to what is going on. This is essentially what the very first iteration of the reducer function is doing:
output.reduce((acc, ["mark johansson", "waffle iron", "80", "2"])=>{
acc["mark johansson"] = acc["mark johansson"] || [];
acc["mark johansson"].push({
name: "waffle iron",
price: "80",
quant: "2"
});
return acc
},{})
Imagine that the first line of the reducer function just said acc["mark johansson"] = acc["mark johansson"]. Since there is no key on the object acc with the name "mark johansson", after evaluating that expression the object acc would look like:
acc = {
"mark johansson": undefined
}
However, by adding || [] onto the end of the expression, we can evaluate whether acc["mark johansson"] is truthy before we actually set the key/value pair. Since undefined is falsy, the || operater kicks in and we get this instead:
acc = {
"mark johansson": []
}
Do you see the difference? With the OR operator we are saying: "either acc["mark johansson"] exists and is therefore truthy, and we set it as itself, OR it is falsy and we set it as a blank array". The rest of the code should be fairly self explanatory. The key/value pair is now guaranteed to exist and we can push the data object to the array. Any further lines which reference acc["mark johansson"] will target the already existing entry.
It helps if you console log after each step to see what is going on:
let output = [
["mark johansson", "waffle iron", "80", "2"],
["mark johansson", "blender", "200", "1"],
["mark johansson", "knife", "10", "4"],
["Nikita Smith", "waffle iron", "80", "1"],
["Nikita Smith", "knife", "10", "2"],
["Nikita Smith", "pot", "20", "3"]
]
let result = output.reduce((acc, line) => {
console.log("acc value at start:", acc, "current line value:", line)
acc[line[0]] = acc[line[0]] || []
//either a new key will appear with an empty array as the value, or the acc will appear unchanged
console.log("acc value after key configuration:", acc)
acc[line[0]].push({
name: line[1],
price: line[2],
quant: line[3]
})
//there will be one new object within one of the keys' array value;
//acc will appear the same in the first console log of the next cycle
console.log("acc after current line values pushed as new object:", acc)
return acc
}, {})
console.log(JSON.stringify(result))
The code snippet above has notes detailing what to look for in the console logs, below is the actual line by line explaination:
//pass {} as the initial value (acc) and cycle through each inner array within output array as current value(line)
let result = output.reduce((acc,line)=>{
//if line[0], which is the name in each inner array, already exists as a key
//then equate key to equal its current value as to not overwrite
//otherwise equate key to equal a new empty array
//basically, whenever line[0] value changes to another name, a new key will be created
acc[line[0]] = acc[line[0]] || []
//now push the other values of the current inner array
//into the key which matches that inner arrays first value(which is the name)
acc[line[0]].push({
name: line[1],
price: line[2],
quant: line[3]
})
//pass the object to the next cycle
return acc
},{})
I have a JSON array like this:
[
{
"id": "1",
"name": "A"
},
{
"id": "2",
"name": "B"
},
{
"id": "3",
"name": "C"
},
{
"id": "4",
"name": "D"
},
....
....
{
"id": "n",
"name": "X"
}
]
I'm looking for a slice() based function that gives the last 20 item of this JSON array
function getLast(array,x){return array.slice(array.length-x)}
Just use the slice function starting with the array length minus the number of elements you want to extract.
A simple way with filters:
filteredList = list.filter((_, index) => {
return index >= list.length - 20
})
If you just need the last X items in an array I'm not sure you need filter, you can use .slice eg [1,2,3,4,5,6,7,8,9,10].slice(-5) will return [6,7,8,9,10]
One option is to use splice or slice:
// Initialize array
let arr = new Array(50).fill().map((v,i)=>i)
// Pick off last 20 elements
console.log('Last:' + arr.slice(-20))
Note: splice modifies the existing array; if you don't want to modify the existing array use slice
Another example
let arr = new Array(50).fill().map((v,i)=>i+1) // [1,2,..50]
Array.prototype.last = function(n){
return this.slice(-n)
};
console.log( arr.last(20) )
I have a two dimensional json that looks like:
[[{"ID":1,"Name":"....","Ct":123, "Time":"2018-05-07T00:00:00"},
{"ID":2,"Name":"....","Ct":123, "Time":"2018-05-07T00:01:00"}],
[{"ID":3,"Name":"....","Ct":123, "Time":"2018-05-07T00:02:00"},
{"ID":4,"Name":"....","Ct":123, "Time":"2018-05-07T00:03:00"}]]
It is serialized from C# object. Anyway I try to iterate over this json like:
for (var key in data) // first loop
{
var item = data[key];
for (var key2 in item) // second loop
{
// some code...
}
}
Where the first loop should iterate two times: during first iteration the second loop should iterate over two objects with IDs 1 and 2; and during second iteration over objects with IDs 3 and 4.
I guess the problem is that first loop doesn't have Key because first loop iterates only one time and second loop iterates over objects with IDs 1,2,3,4.
How can I iterate over this as two dimensional tab then?
[EDIT]
I made a mistake during iterating inside second loop and it looked like it doesn't iterate. The solution above is correct.
For loop should work but you can also use maps/forEach/for-of to iterate over arrays:
var input = [
[{"ID":1,"Name":"....","Ct":123, "Time":"2018-05-07T00:00:00"},
{"ID":2,"Name":"....","Ct":123, "Time":"2018-05-07T00:01:00"}],
[{"ID":3,"Name":"....","Ct":123, "Time":"2018-05-07T00:02:00"},
{"ID":4,"Name":"....","Ct":123, "Time":"2018-05-07T00:03:00"}]
];
input.map(outElem => {
console.log('======== outter ========');
outElem.map(inElem => {
console.log('======== inner ========');
console.log(inElem.ID);
});
});
// Output:
======== outter ========
======== inner ========
1
======== inner ========
2
======== outter ========
======== inner ========
3
======== inner ========
4
Or forEach:
input.forEach(outElem => {
console.log('======== outter ========');
outElem.forEach(inElem => {
console.log('======== inner ========');
console.log(inElem.ID);
});
});
Or for of:
for (const outElem of input) {
console.log('======== outter ========');
for (const inElem of outElem) {
console.log('======== inner ========');
console.log(inElem.ID);
}
}
var data = [
[{
"ID": 1,
"Name": "....",
"Ct": 123,
"Time": "2018-05-07T00:00:00"
},
{
"ID": 2,
"Name": "....",
"Ct": 123,
"Time": "2018-05-07T00:01:00"
}
],
[{
"ID": 3,
"Name": "....",
"Ct": 123,
"Time": "2018-05-07T00:02:00"
},
{
"ID": 4,
"Name": "....",
"Ct": 123,
"Time": "2018-05-07T00:03:00"
}
]
];
for (var key in data) // first loop
{
var item = data[key];
console.log('======== outter ========');
for (var key2 in item) // second loop
{
console.log('======== inner ========');
console.log(item[key2].ID);
}
}
# Problem
Hello. I have a JSON response containing a varying amount of objects (a set of indicators), each containing a fixed set of other objects (geometries) that each contain properties (one of which is 'score').
I'm trying to gather these 'score' properties in order to later do stuff such as min/mean/max by geometry.
# Sample
Here's an example (keeping in mind there could be more than two indicators):
let data = [ {
{
"indicator": "A",
"geom": "1",
"score": 1
},
{
"indicator": "A",
"geom": "2",
"score": 2
} }, {
{
"indicator": "B",
"geom": "1",
"score": 3
},
{
"indicator": "B",
"geom": "2",
"score": 4
} } ]
# Expected result
The result I'm looking for would be something like this, with concatenated values originating from different sub-objects :
let expectedResult = {
{
"indicator": ["A", "B"],
"geom": "1",
"score": [1,3]
},
{
"indicator": ["A", "B],
"geom": "2",
"score": [2,4]
} }
# My (no good) solution
My current, ugly buggy solution is to create an array with all geom ids :
let id = data[0].map(obj => obj.geom);
Then get a complete list of all key-value :
let keyval;
data.map((indic) => { indic.map((geom) =>
{ keyval.push([car.geom, car.score])})});
And finally combine geom id var with values that have identical id (and slice off the redundant id) :
id.map((geom, idx) => {keyval.map((arr) => {
if (car === arr[0]) { id.push(geom, arr.splice(0,1)})
}
})
});
Would anyone know of a more elegant/efficient.. and more importantly working solution ? During my research saw a lot of Array.prototype.reduce(), but didn't figure out how to use it in such a nested configuration.
Thanks,
O.
Use Array#reduce to collect the values into a Map, then use Map#values, and the spread syntax to convert back to an array:
const data = [[{"indicator":"A","geom":"1","score":1},{"indicator":"A","geom":"2","score":2}],[{"indicator":"B","geom":"1","score":3},{"indicator":"B","geom":"2","score":4}]];
const result = [...[].concat(...data).reduce((map, o) => {
const item = map.get(o.geom) || { geom: o.geom, indicator: [], score: [] }; // get the item from the map, or create a new one
item.indicator.push(o.indicator);
item.score.push(o.score);
return map.set(o.geom, item); // set the item and return the map reference
}, new Map).values()]; // get the map values iterator, and use spread (...) to get an array
console.log(result);