JavaScript - iterate over object - javascript

I have an object like this that I get from an external endpoint so cannot change:
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
}
I need to get it into a format like:
1: {
1: { "time": 1, "day": 1 },
2: { "time": 2, "day": 1 },
3: { "time": 3, "day": 1 },
},
2: {
1: { "time": 1, "day": 2 },
2: { "time": 2, "day": 2 },
3: { "time": 3, "day": 2 },
}
Where the first key is the day, and the second is the time.
My attempt doesn't work
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
}
let test = {}
let defaultRow = {
1: {},
2: {}
}
Object.keys(obj).forEach((key) => {
if (!test[obj[key]["day"]]) {test[obj[key]["day"]] = defaultRow}
test[obj[key]["day"]][obj[key]["time"]] = obj[key]
})
console.log(test)
Both days contain the same data for some reason. How can I achieve this? Any help is appreciated!

Both days contain the same data for some reason
Because you assign the same object reference defaultRow to each row. A simple fix is turn defaultRow into a function that returns a new object each time
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
}
let test = {}
let defaultRow = function() {
return {
1: {},
2: {}
}
}
Object.keys(obj).forEach((key) => {
if (!test[obj[key]["day"]]) {
test[obj[key]["day"]] = defaultRow();
}
test[obj[key]["day"]][obj[key]["time"]] = obj[key]
})
console.log(test)
Simple example of the problem
var obj ={a:1},
foo = obj,
bar = obj;
foo.a=2;
console.log(bar.a) //returns 2 because is same object as foo

You could take the objects and build a new object with a new structure.
var object = { 0: { time: 1, day: 1 }, 1: { time: 2, day: 1 }, 2: { time: 3, day: 1 }, 3: { time: 1, day: 2 }, 4: { time: 2, day: 2 }, 5: { time: 3, day: 2 } },
result = Object.values(object).reduce(
(r, o) => ((r[o.day] = r[o.day] || {})[o.time] = o, r),
{}
);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Convert into an array of object using Object#values, and iterate the array using Array#reduce to convert to the desired format.
const obj = {"0":{"time":1,"day":1},"1":{"time":2,"day":1},"2":{"time":3,"day":1},"3":{"time":1,"day":2},"4":{"time":2,"day":2},"5":{"time":3,"day":2}};
const result = Object.values(obj).reduce((r, o) => {
r[o.day] = Object.assign(r[o.day] || {}, { [o.time]: o });
return r;
}, {});
console.log(result);

Since you want your nested objects to be plain objects not arrays the solution to get last index is rather involved.
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
}
console.log(
Object.values(obj).reduce(
(acc, value) => {
const holder = acc[value.day] || (acc[value.day] = { })
const keys = Object.keys(holder).map(key => +key)
holder[keys.length ? (Math.max(...keys) + 1) : 1] = value
return acc
},
{}
)
)

To solve this problem, you need to iterate through your object and make value of day as your outer and increment the inner key as per the number of keys in the Object which you can get by Object.keys.length.
Here is the sample code.
let obj = {
0: {
"time": 1,
"day": 1,
},
1: {
"time": 2,
"day": 1,
},
2: {
"time": 3,
"day": 1,
},
3: {
"time": 1,
"day": 2,
},
4: {
"time": 2,
"day": 2,
},
5: {
"time": 3,
"day": 2,
}
}
let newObj = {};
for (let key in obj) {
if(typeof newObj[obj[key]["day"]] !== "object") {
newObj[obj[key]["day"]] = {};
}
let index = Object.keys(newObj[obj[key]["day"]]).length + 1;
newObj[obj[key]["day"]][index] = obj[key];
}
console.log(newObj);

This will work for you
var result = Object.values(obj).reduce((count, currentValue) => {
count[currentValue.day] = count[currentValue.day] || {};
count[currentValue.day][Object.keys(count[currentValue.day]).length + 1] = currentValue;
return count
}, {});

There is start with :
const result = Object.values(obj).reduce((accumulator, currentValue) => {
if (!accumulator[currentValue.day]) accumulator[currentValue.day] = []
accumulator[currentValue.day].push(currentValue)
return accumulator
}, {})
console.log(result)
This is not THE solution but a starting kit :)
More infos:
- MDN reduce
- Stackoverflow search

// your code goes here
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
}
obj = Object.values(obj).reduce(function(newObject, val){
//console.log(val.day, val.time, newObject);
if(newObject[val['day']] == undefined)
newObject[val['day']] ={};
newObject[val['day']][val['time']] = val;
//console.log(val.day, val.time, newObject);
return newObject;
},{})
console.log(obj);
/*
output
{ '1':
{ '1': { time: 1, day: 1 },
'2': { time: 2, day: 1 },
'3': { time: 3, day: 1 } },
'2':
{ '1': { time: 1, day: 2 },
'2': { time: 2, day: 2 },
'3': { time: 3, day: 2 } } }
*/

I don't really know why you need an object with properties 1...n instead of just using arrays for this purpose. I would solve it like this:
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
};
function orderByDay(obj){
const result = {};
Object.keys(obj).forEach(function(key){
let day = obj[key].day;
if(result.hasOwnProperty(day)){
result[day].push(obj[key]);
}else{
result[day] = [obj[key]];
}
});
return result;
}
console.log(orderByDay(obj));

My rather readable version
let obj = {
0: { "time": 1, "day": 1, },
1: { "time": 2, "day": 1, },
2: { "time": 3, "day": 1, },
3: { "time": 1, "day": 2, },
4: { "time": 2, "day": 2, },
5: { "time": 3, "day": 2, }
}
let test = {}
for (let o in obj) {
const day = obj[o].day;
const time = obj[o].time;
if (test[day]==undefined) {
test[day] = {}
}
test[day][time] = {day:day,time:time}
}
console.log(test)

Related

Merge arrays matching a particular key value in JavaScript

I have an array which is like this:
var arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
I want to match the primary key which is heredate. Matching this I want to merge the rest of the key values and get this following output:
[{
"date": "JAN",
"value": [5, 4],
"weight": [3, 23]
}, {
"date": "FEB",
"value": [9, 10],
"weight": [1, 30]
}]
I have written a function like this but can't figure out how to concat the key values:
var arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
const transform = (arr, primaryKey) => {
var newValue = [];
for (let i = 0; i < arr.length; i++) {
for (let j = 1; j < arr.length; j++) {
if (primaryKey[i] === primaryKey[j]) {
newValue.push({
...arr[i],
...arr[j]
});
}
}
}
return newValue
};
console.log(transform(arr,'date'))
Using Array#reduce, iterate over the list while updating a Map where the key is the primary-key and the value is the grouped object. In every iteration, create/update the pair.
Using Map#values, return the list of grouped objects
const transform = (arr, primaryKey) => [...
arr.reduce((map, { [primaryKey]: key, ...e }) => {
const { [primaryKey]: k, ...props } = map.get(key) ?? {};
for(let prop in e) {
props[prop] = [...(props[prop] ?? []), e[prop]];
}
map.set(key, { [primaryKey]: key, ...props });
return map;
}, new Map)
.values()
];
const arr = [ { "date": "JAN", "value": 5, "weight": 3 }, { "date": "JAN", "value": 4, "weight": 23 }, { "date": "FEB", "value": 9, "weight": 1 }, { "date": "FEB", "value": 10, "weight": 30 } ];
console.log( transform(arr, 'date') );
The following code should work:
const transform = (arr, primaryKey) => {
var newValue = [];
for(let i = 0; i < arr.length; i++){
arr[i]["value"] = [arr[i]["value"]];
arr[i]["weight"] = [arr[i]["weight"]];
}
newValue.push(arr[0])
for(let i = 1; i < arr.length; i++){
let contains = false;
for(let j = 0; j < newValue.length; j++){
if(newValue[j][primaryKey] == arr[i][primaryKey]){
newValue[j]["value"].push(arr[i]["value"][0]);
newValue[j]["weight"].push(arr[i]["weight"][0]);
contains = true;
}
}
if(!contains){
newValue.push(arr[i]);
}
}
return newValue
};
var arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
var newthing = transform(arr,"date");
console.log(newthing);
Output:
[ { date: 'JAN', value: [ 5, 4 ], weight: [ 3, 23 ] },
{ date: 'FEB', value: [ 9, 10 ], weight: [ 1, 30 ] } ]
The way this code works is that first, we turn the values of the keys for "value" and "weight" into lists.
Then, we begin by pushing the first element of arr into newValue.
From here, we do a nested for loop to iterate through the remaining of arr and newValue:
If the value of "date" for every element of arr already exists in newValue, then we will push in the values of "value" and "weight" that belongs to arr.
However, if it does not exist, then we will simply push that element inside of newValue.
I hope this helped answer your question! Pleas let me know if you need any further help or clarification :)
Combining a couple of reduce can also do the same job:
const arr = [{
"date": "JAN",
"value": 5,
"weight": 3
}, {
"date": "JAN",
"value": 4,
"weight": 23
}, {
"date": "FEB",
"value": 9,
"weight": 1
}, {
"date": "FEB",
"value": 10,
"weight": 30
}]
const arrayMappedByDate = arr.reduce((acc, curData) => {
if (acc[curData.date]) {
acc[curData.date].push(curData)
} else {
acc[curData.date] = [curData]
}
return acc
}, {})
const transformedArray = Object.entries(arrayMappedByDate).map(([dateInit, data]) => {
const normalized = data.reduce((acc, cur) => {
if (acc.date) {
acc.value.push(cur.value)
acc.weight.push(cur.weight)
} else {
acc = {
date: cur.date,
value: [cur.value],
weight: [cur.weight]
}
}
return acc
}, {})
return { [dateInit]: normalized }
})
console.log(transformedArray)

Getting dates from JSON for D3

My JSON file has the dates separated like this:
"time": {
"date": {
"year": 2018,
"month": 2,
"day": 25
},
"time": {
"hour": 10,
"minute": 19,
"second": 6,
"nano": 19000000
}
},
The tutorial I used to get a line graph in d3 going was in this link:
https://datawanderings.com/2019/10/28/tutorial-making-a-line-chart-in-d3-js-v-5/
Using the code below:-
const timeConv = d3.timeParse("%d-%b-%Y");
const dataset = d3.csv(datacsv);
dataset.then(function(data) {
var slices = data.columns.slice(1).map(function(id) {
return {
id: id,
values: data.map(function(d){
return {
date: timeConv(d.date),
measurement: +d[id]
};
})
};
});
});
How could I use the same code but use the JSON file with the separated date values?
Just make up the actual date string from the separate dates:
return {
date: timeConv(d.time.date.day + '-' + d.time.date.month + '-' + d.time.date.year),
measurement: +d[id]
};
Since the month is not described as the abbreviated month name, you need to change timeConv as
const timeConv = d3.timeParse("%d-%m-%Y");
An json data example:
let dataset = [{
"id": 1,
"time": {
"date": {
"year": 2018,
"month": 2,
"day": 25
},
"time": {
"hour": 10,
"minute": 19,
"second": 6,
"nano": 19000000
}
}
}, {
"id": 2,
"time": {
"date": {
"year": 2019,
"month": 2,
"day": 25
},
"time": {
"hour": 10,
"minute": 19,
"second": 6,
"nano": 19000000
}
}
}]
const timeConv = d3.timeParse("%d-%m-%Y");
newData = dataset.map(function(d) {
return {
date: timeConv(d.time.date.day + '-' + d.time.date.month + '-' + d.time.date.year),
measurement: +d.id
}
})
console.log(newData)
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

what is the best way to remove certain objects from a nested object?

I have the following nested object:
{
"cards": {
"1": {
"x": 247,
"y": 213,
"properties": {
"id": 1,
"name": "a",
"intros": {
"intro_0": {
"id": 1,
"cardId": 1
},
"intro_1": {
"id": 2,
"cardId": 1
},
"intro_3": {
"id": 24,
"cardId": 1
}
},
"exits": {
"exit_0": {
"id": 1,
"cardId": 1
},
"exit_1": {
"id": 22,
"cardId": 1
}
},
"mixins": {
"mixin_0": {
"id": 23,
"cardId": 1
}
}
}
},
"3": {
"x": 762,
"y": 187,
"properties": {
"id": 1,
"name": "x",
"intros": {
"intro_0": {
"id": 1,
"cardId": 1
},
"intro_1": {
"id": 263,
"cardId": 1
}
},
"exits": {
"exit_0": {
"id": 1,
"cardId": 1
},
"exit_1": {
"id": 2,
"cardId": 1
}
},
"mixins": {
"mixin_0": {
"id": 3,
"cardId": 1
}
},
}
},
"4": {
"x": 1200,
"y": 187,
"properties": {
"id": 1,
"name": "j",
"intros": {
"intro_0": {
"id": 1,
"cardId": 1
},
"intro_1": {
"id": 276,
"cardId": 1
}
},
"exits": {
"exit_0": {
"id": 1,
"cardId": 1
},
"exit_1": {
"id": 2,
"cardId": 1
}
},
"mixins": {
"mixin_0": {
"id": 3,
"cardId": 1
}
}
}
}
}
}
I am trying to go through the object to extract only the first of the "intro" and the first of the "exit" and modify the original object so that it only contains these with the rest of the data. Or what is the same, I would like to remove all the "intros" except the first one and all the "exits" except the first one.
To extract the values I want I have made 3 for in ... but when it comes to modifying the original object I think I've made it too complicated and there must be some easier way.
const cards: any = Object.values(data.cards);
for (const indexCard in cards) {
if (cards.hasOwnProperty(indexCard)) {
const card = cards[indexCard];
const cardIntros = card.properties.intros;
for (const introIndex in cardIntros) {
if (cardIntros.hasOwnProperty(introIndex) && introIndex === 'intro_0') {
const firstIntro = cardIntros[introIndex];
const cardsExits = card.properties.exits;
for (const exitIndex in cardsExits) {
if (cardsExits.hasOwnProperty(exitIndex) && exitIndex === 'exit_0') {
const firstExit = cardsExits[exitIndex];
}
}
}
}
}
}
Does anyone see an easier way to remove the "intros" and "exits" I want to remove, in order to generate a new object that only contains the desired "intros" and "exits"?
Thank you in advance!
You could create recursive function that will check if the current value is object and then also if the some of the keys of that object is equal to the keys provided by the parameter. If it does include that key it will delete other keys from that object. This will work for any level of nesting.
const data = {"cards":{"1":{"x":247,"y":213,"properties":{"id":1,"name":"a","intros":{"intro_0":{"id":1,"cardId":1},"intro_1":{"id":2,"cardId":1},"intro_3":{"id":24,"cardId":1}},"exits":{"exit_0":{"id":1,"cardId":1},"exit_1":{"id":22,"cardId":1}},"mixins":{"mixin_0":{"id":23,"cardId":1}}}},"3":{"x":762,"y":187,"properties":{"id":1,"name":"x","intros":{"intro_0":{"id":1,"cardId":1},"intro_1":{"id":263,"cardId":1}},"exits":{"exit_0":{"id":1,"cardId":1},"exit_1":{"id":2,"cardId":1}},"mixins":{"mixin_0":{"id":3,"cardId":1}}}},"4":{"x":1200,"y":187,"properties":{"id":1,"name":"j","intros":{"intro_0":{"id":1,"cardId":1},"intro_1":{"id":276,"cardId":1}},"exits":{"exit_0":{"id":1,"cardId":1},"exit_1":{"id":2,"cardId":1}},"mixins":{"mixin_0":{"id":3,"cardId":1}}}}}}
function first(data, props = []) {
return Object.entries(data).reduce((r, [k, v]) => {
if (typeof v == 'object' && v != null) {
if (Array.isArray(v)) {
r[k] = v.map(e => first(e, props))
} else {
const value = { ...v }
const check = props.some(p => Object.keys(v).includes(p))
if (check) {
for (let i in value) {
if (!props.includes(i)) {
delete value[i]
}
}
}
r[k] = first(value, props)
}
} else r[k] = v
return r;
}, {})
}
const result = first(data, ['intro_0', 'exit_0'])
console.log(result)

lodash merge and combine objects

I have an array of objects as below that I read from my database using sequelize ORM:
I want to have all my videos from a section, but the better I can return using sequelize is :
[{
"id": 2,
"name": "Ru",
"subsection": 1,
"Video": {
"id": 11,
"source": "sourrrccrsss22222",
"videoSubSection": 2
}
},
{
"id": 2,
"name": "Ru",
"subsection": 1,
"Video": {
"id": 12,
"source": "sourrrccrsss111",
"videoSubSection": 2
}
},
{
"id": 1,
"name": "Oc",
"subsection": 1,
"Video": {
"id": 13,
"source": "sourrrcc",
"videoSubSection": 1
}
},
{
"id": 1,
"name": "Oc",
"subsection": 1,
"Video": {
"id": 14,
"source": "sourrrcc",
"videoSubSection": 1
}
}]
Is there a way to merge and combine the objects in my array to obtain something like this :
[{
"id": 2,
"name": "Ru",
"subsection": 1,
"Video": [{
"id": 11,
"source": "sourrrccrsss22222",
"videoSubSection": 2
},{
"id": 12,
"source": "sourrrccrsss111",
"videoSubSection": 2
}]
},
{
"id": 1,
"name": "Oc",
"subsection": 1,
"Video": [{
"id": 13,
"source": "sourrrcc",
"videoSubSection": 1
},{
"id": 14,
"source": "sourrrcc",
"videoSubSection": 1
}]
}
The function that approach the most is _.mergeWith(object, sources, customizer) but the main problem I have is that I have on object and need to merge this object.
In plain Javascript, you can use Array#forEach() with a temporary object for the arrays.
var data = [{ id: 2, name: "Ru", subsection: 1, Video: { id: 11, source: "sourrrccrsss22222", VideoSubSection: 2 } }, { id: 2, name: "Ru", subsection: 1, Video: { id: 12, source: "sourrrccrsss111", VideoSubSection: 2 } }, { id: 1, name: "Oc", subsection: 1, Video: { id: 13, source: "sourrrcc", VideoSubSection: 1 } }, { id: 1, name: "Oc", subsection: 1, Video: { id: 14, source: "sourrrcc", VideoSubSection: 1 } }],
merged = function (data) {
var r = [], o = {};
data.forEach(function (a) {
if (!(a.id in o)) {
o[a.id] = [];
r.push({ id: a.id, name: a.name, subsection: a.subsection, Video: o[a.id] });
}
o[a.id].push(a.Video);
});
return r;
}(data);
document.write('<pre>' + JSON.stringify(merged, 0, 4) + '</pre>');
Maybe try transform():
_.transform(data, (result, item) => {
let found;
if ((found = _.find(result, { id: item.id }))) {
found.Video.push(item.Video);
} else {
result.push(_.defaults({ Video: [ item.Video ] }, item));
}
}, []);
Using reduce() would work here as well, but transform() is less verbose.
You can do it this way (test is your db output here)
var result = [];
var map = [];
_.forEach(test, (o) => {
var temp = _.clone(o);
delete o.Video;
if (!_.some(map, o)) {
result.push(_.extend(o, {Video: [temp.Video]}));
map.push(o);
} else {
var index = _.findIndex(map, o);
result[index].Video.push(temp.Video);
}
});
console.log(result); // outputs what you want.

How can I sort the summed up number from JSON

I have the JSON look like below. I'm finding the way to sort the numberOfPoint after I summed up.
{ "data": [
{
"id": {
"year": 2015,
"Name": "A"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2014,
"Name": "C"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2014,
"Name": "B"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2014,
"Name": "B"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2013,
"Name": "C"
},
"numberOfPoint": 2
}]
}
obj is my JSON.
var result = {};
obj.data.forEach(function (a) { result[a["id"]["Name"]] = (result[a["id"]["Name"]] || 0) + a["numberOfPoint"]; });
result is look like below
{
"A": 2,
"C": 4,
"B": 4
}
How can I sort this from largest to lowest numberOfPoint?
I want the result like this.
{
"B": 4,
"C": 4,
"A": 2
}
Thank you in advance.
You can do as below :
var result={
"A": 2,
"C": 4,
"B": 4,
"D": 3
}
var temp = [];
$.each(result, function(key, value) {
temp.push({v:value, k: key});
});
temp.sort(function(a,b){
if(a.v < b.v){ return 1}
if(a.v > b.v){ return -1}
return 0;
});
$.each(temp, function(index, obj) {
alert(obj.k + "-" + obj.v);
});
Here is JSFiddle
Maybe this will help.
obj.data.sort(function(a, b) {
return a.id.Name < b.id.Name;
});
for(var i in obj.data) {
console.log(obj.data[i].id.Name);
}
var obj = { "data": [
{
"id": {
"year": 2015,
"Name": "A"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2014,
"Name": "C"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2014,
"Name": "B"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2014,
"Name": "B"
},
"numberOfPoint": 2
},
{
"id": {
"year": 2013,
"Name": "C"
},
"numberOfPoint": 2
}] }
var result = {};
obj.data.forEach(function (a) {
result[a["id"]["Name"]] = (result[a["id"]["Name"]] || 0) + a["numberOfPoint"];
});
var temp = [];
$.each(result, function(key, value) {
temp.push({v:value, k: key});
}); temp.sort(function(a,b){ if(a.v < b.v){ return 1}
if(a.v > b.v){ return -1}
return 0; });
$.each(temp, function(index, obj) {
alert(obj.k + "-" + obj.v); });
I hope you like this..if you like then don't forget to vote me..and thanks #Butani Vijay for nice sorting. i have implemented in common...

Categories

Resources