I need to make a function to iterate an array of x objects then compare the date inside the objects and separate in different arrays so I can show separately in the HTML, this is my object:
[{"id":1,"date":"2020-02-06","value":131},{"id":2,"date":"2020-02-06","value":135},{"id":3,"date":"2020-02-06","value":141},{"id":4,"date":"2020-02-05","value":151},{"id":6,"date":"2020-02-05","value":155}]
I want something like this:
obj1 = [{"id":1,"date":"2020-02-06","value":131},{"id":2,"date":"2020-02-06","value":135},{"id":3,"date":"2020-02-06","value":141}]
obj2 = [{"id":4,"date":"2020-02-05","value":151},{"id":6,"date":"2020-02-05","value":155}]
my code:
// global variables
json = [{
"id": 1,
"date": "2020-02-06",
"value": 131
}, {
"id": 2,
"date": "2020-02-06",
"value": 135
}, {
"id": 3,
"date": "2020-02-06",
"value": 141
}, {
"id": 4,
"date": "2020-02-05",
"value": 151
}, {
"id": 6,
"date": "2020-02-05",
"value": 155
}];
obj1 = [];
obj2 = [];
for (const x of json) {
if (x.date != x.date) {
obj1.push(x)
} else {
obj2.push(x)
}
}
console.log(obj1);
console.log(obj2);
in result always the items push into the obj1..
any help is welcome.
The typical solution for this is to group them by the key and push them to an array. Below is an example using Array reduce and Object.values to get it down to the two arrays.
var items = [
{"id":1,"date":"2020-02-06","value":131},
{"id":2,"date":"2020-02-06","value":135},
{"id":3,"date":"2020-02-06","value":141},
{"id":4,"date":"2020-02-05","value":151},
{"id":6,"date":"2020-02-05","value":155}
]
var dateGroups = items.reduce( function (dates, item) {
dates[item.date] = dates[item.date] || []
dates[item.date].push(item)
return dates
}, {})
var results = Object.values(dateGroups)
console.log(results)
You could goup by date and get an array of arrays.
var data = [{ id: 1, date: "2020-02-06", value: 131 }, { id: 2, date: "2020-02-06", value: 135 }, { id: 3, date: "2020-02-06", value: 141 }, { id: 4, date: "2020-02-05", value: 151 }, { id: 6, date: "2020-02-05", value: 155 }],
grouped = data.reduce((r, o) => {
var group = r.find(([{ date }]) => date === o.date);
if (!group) r.push(group = []);
group.push(o);
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use reduce to build an object that maps each unique date to its respective data items, having, basically, a group by date:
const data = [{"id":1,"date":"2020-02-06","value":131},{"id":2,"date":"2020-02-06","value":135},{"id":3,"date":"2020-02-06","value":141},{"id":4,"date":"2020-02-05","value":151},{"id":6,"date":"2020-02-05","value":155}];
const groupedData = data.reduce((acc, curr) => ({
...acc,
[curr.date]: [...(acc[curr.date] || []), curr]
}), {});
console.log(groupedData);
We also use above the spread syntax and computed property names to make the code shorter.
Related
Hi I have array of objects and I am trying to loop through them and have similar key values in new object here is example of data that I have.
let newDats = [{"ID":1, "Name": "Ahmed", "Age":17, "Score":84, "Absentee":3},
{"ID":2, "Name": "Hassan", "Age":15, "Score":87, "Absentee":2},
{"ID":3, "Name": "Aisha", "Age":18, "Score":86, "Absentee":2}]
And so on. However, what I want is something as:
data = [{ID:[1,2,3], Names:["Ahmed", "Hassan","Aisha"],
Ages:[17,15,18]}]
And so forth. My end goal is to perform basic descriptive statistics on the numeric parts of the data so if there is better ideas I would love to hear I am knida newbie to js .
PS
Column names (object keys ) can be more than this and unknown so I want something dynamic.
Thanks in advance.
You can use the function below. Just pass the array you want to sort.
function dataSorter(dataArr) {
const data = {};
for (const item in dataArr[0]) data[item] = [];
for (let i = 0; i < dataArr.length; i++) {
for (const item in dataArr[i]) {
data[item].push(dataArr[i][item]);
}
}
return data;
}
/* Example Below */
let newDats = [{
ID: 1,
Name: "Ahmed",
Age: 17,
Score: 84,
Absentee: 3
},
{
ID: 2,
Name: "Hassan",
Age: 15,
Score: 87,
Absentee: 2
},
{
ID: 3,
Name: "Aisha",
Age: 18,
Score: 86,
Absentee: 2
},
];
console.log(dataSorter(newDats));
.as-console-wrapper { max-height: 100% !important; top: 0; } /* Ignore this */
Something like:
let newDats = [{"ID":1, "Name": "Ahmed", "Age":17, "Score":84, "Absentee":3},
{"ID":2, "Name": "Hassan", "Age":15, "Score":87, "Absentee":2},
{"ID":3, "Name": "Aisha", "Age":18, "Score":86, "Absentee":2}];
data = {};
keys = [];
for(let keyVal in newDats[0]) {
keys.push(keyVal)
data[keyVal] = [];
}
newDats.forEach(dt => {
keys.forEach( kv => {
data[kv].push(dt[kv])
})
})
console.log(data)
You can simply reduce() the array of objects, iterating over all Object.keys() of each element and pushing them to the property array of the accumulator. This allows for each object having different keys.
let newDats = [{ "ID": 1, "Name": "Ahmed", "Age": 17, "Score": 84, "Absentee": 3 }, { "ID": 2, "Name": "Hassan", "Age": 15, "Score": 87, "Absentee": 2 }, { "ID": 3, "Name": "Aisha", "Age": 18, "Score": 86, "Absentee": 2 }];
const result = newDats.reduce((acc, o) => (
Object.keys(o).forEach(k => (acc[k] ??= []).push(o[k])), acc), {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Note the use of the logical nullish assignment operator (??=) which can be replaced by a logical OR short circuit for compatibility.
//acc[k] ??= []
acc[k] = acc[k] || []
Array of objects that I got
[
{
"id": 1,
"price": 100
},
{
"id": 1,
"price": 80
},
{
"id": 2,
"price": 8
},
{
"id": 1,
"price": 85
}
]
Array of objects that I am trying to do
[
{
"id": 1,
"price": 88.33 // AVERAGE VALUE BETWEEN DUPLICATED OBJECTS
},
{
"id": 2,
"price": 8
}
]
I am merging and getting the average price for duplicated objects.
What I have done:
I have tried to use filter() function but I removed the duplicated without merging the prices.
If you want to avoid extra loops and extra properties is not a problem, you can use a getter for each object as follow:
You can use the function Array.prototype.reduce for grouping objects by id and the function Object.values for extracting the grouped values.
The getter price calculates the average when this property is accessed.
Extra properties:
{
count: Integer // count of repeated ids.
sum: Double // total sum of prices
}
const arr = [ { "id": 1, "price": 100 }, { "id": 1, "price": 80 }, { "id": 2, "price": 8 }, { "id": 1, "price": 85 } ],
result = Object.values(arr.reduce((r, {id, price}) => {
let current = (r[id] || (r[id] = {id, sum: 0, count: 0, get price() {
return this.sum / this.count;
}}));
current.sum += price;
current.count++;
return r;
}, {}));
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can use .reduce() with an ES6 Map. By using reduce() you can accumulate all objects into a Map, where the key is the id from the object and the value is an accumulated array of price values for the given id. You can then convert the Map back into an array using Array.from(), where you can provide a mapping function to convert the [key, value] pairs from the map into an object. The object's price key will be the sum of all numbers in the value array (arr) divided by the length of the array, which will give you the average.
See example below:
const arr = [ { "id": 1, "price": 100 }, { "id": 1, "price": 80 }, { "id": 2, "price": 8 }, { "id": 1, "price": 85 } ];
const res = Array.from(arr.reduce((m, {id, price}) => {
return m.set(id, [...(m.get(id) || []), price]);
}, new Map), ([id, arr]) => ({id, price: arr.reduce((t, n) => t+n, 0) / arr.length}));
console.log(res);
Use forEach loop and build an object with keys as id and aggregate price.
Use Object.values of above object and calculate the averages.
const data = [
{
id: 1,
price: 100,
},
{
id: 1,
price: 80,
},
{
id: 2,
price: 8,
},
{
id: 1,
price: 85,
},
];
const process = (arr) => {
const res = {};
arr.forEach(({ id, price }) => {
res[id] ??= { id, sum: 0, count: 0 };
res[id].sum += price;
res[id].count += 1;
});
return Object.values(res).map(({ id, sum, count }) => ({
id,
price: sum / count,
}));
};
console.log(process(data));
I have been playing around with some Nested Set Models (NSM). One thing I wanted to do is to be able to generate a NSM from a given JavaScript object.
For example, given the following object:
var data = {
Clothes: {
Jackets: {
Waterproof: true,
Insulated: true
},
Hats: true,
Socks: true
},
}
I'd like to generate an array of objects like so.
[
{
"name": "Clothes",
"lft": 1,
"rgt": 12
},
{
"name": "Jackets",
"lft": 2,
"rgt": 7
},
{
"name": "Waterproof",
"lft": 3,
"rgt": 4
},
{
"name": "Insulated",
"lft": 5,
"rgt": 6
},
{
"name": "Hats",
"lft": 8,
"rgt": 9
},
{
"name": "Socks",
"lft": 10,
"rgt": 11
}
]
That is - a depth first walk through the object, assigning an ID and counting the left and right edge for each object in the hierarchy. So that each node has a unique ID and the correct lft and rgt values for a NSM.
I've tried various approaches but just can't seem to get the result I am after...I had some success by altering the model to use properties for the node name and child nodes - i.e.
var data2 = {
name: "Clothes",
children: [{
name: "Jackets",
children: [{
name: "Waterproof",
}, {
name: "Insulated"
}]
}, {
name: "Hats"
},
{
name: "Socks"
}
]
};
function nestedSet(o, c, l = 0) {
let n = {
name: o.name,
lft: l + 1
};
c.push(n);
let r = n.lft;
for (var x in o.children) {
r = nestedSet(o.children[x], c, r);
}
n.rgt = r + 1;
return n.rgt;
}
let out = [];
nestedSet(data2, out);
console.log(out)
This gives the correct result but requires altering the input data...is there a way to generate the same Nested Set Model using the original data object?
I actually managed to solve this in the end...I just forgot about it for a long while! Basically all that is required is to reclusively pass the Object.entries as kindly suggested in #CherryDT's comment. This way one can resolve the name/children to build the nested set model as required.
var data = {
Clothes: {
Jackets: {
Waterproof: {},
Insulated: {},
},
Hats: {},
Socks: {},
},
};
function ns(node, stack = [], lft = 0) {
var rgt = ++lft;
var item = {
name: node[0],
lft: lft,
};
stack.push(item);
Object.entries(node[1]).forEach(function (c) {
rgt = ns(c, stack, rgt);
});
item.rgt = ++rgt;
return rgt;
}
var result = [];
ns(Object.entries(data)[0], result);
console.log(result);
This is a follow up to again Push same multiple objects into multiple arrays.
AFter I create my objects:
let objName = ["object1", "object2", "object3"];
let xyzArr = ["xyz1", "xyz2", "xyz3"];
let theArr = [[], [], []];
let objects = [];
objName.forEach((name, index) => {
objects.push({
xyz: xyzArr[index],
arr: theArr[index]
});
});
And push values using #NickParsons solution:
$.getJSON(json, result => {
result.forEach(elem => {
objects.forEach(obj => {
obj.arr.push({
x: elem.date,
y: elem.val2
});
});
});
});
Here I am adding my objects, i.e. x and y based on no condition. But I want to add it based on if indexOf(obj.xyz) = elem.val1.
THis is my JSON:
[
{
"date": "2019-07-21",
"val1": "xyz1_hello",
"val2": 803310
},
{
"date": "2019-07-22",
"val1": "xyz2_yellow",
"val2": 23418
},
{
"date": "2019-07-22",
"val1": "xyz1_hello",
"val2": 6630
},
{
"date": "2019-07-24",
"val1": "xyz2_yellow",
"val2": 4
},
{
"date": "2019-07-21",
"val1": "xyz3_yo",
"val2": 60984
}
]
Is there a way for me to push values to x and y if obj.xyz is LIKE (indexOF) elem.val1 For example, if indexOf(obj.xyz) = elem.val1, then push their corresponding elem.date and elem.val2 data to obj.arr.
Assuming you have some boolean like(a,b) function that decides if two values are similar or not, and your elements are in elems:
objects.forEach(o =>
elems.forEach(e => {
if(like(o.xyz, e.val1)){
o.arr.push({
x: e.date,
y: e.val2
});
}
}));
In the below object, I want to increment the value of data[1]'s val by 1, and leave everything unchanged, how can I achieve it?
const state =
{
"data": [{
"val": 1,
"other": 10
},
{
"val": 11,
"other": 100
},
{
"val": 100,
"other": 1000
}
]
}
I want the mutated object to be like this-
{
"data": [{
"val": 1,
"other": 10
},
{
"val": 10,
"other": 100
},
{
"val": 100,
"other": 1000
}
]
}
I know that I can change the value directly like this- state.data[1].val = state.data[1].val+1, but I want to achieve the same using spread operator, is it possible to achieve it using spread operator?
Somthing like this-
const mutatedState = {
...state,
data: [...state.data]
}
Get the data out of the object. And use like this
const state = { "data": [{ "val": 1, "other": 10 }, { "val": 11, "other": 100 }, { "val": 100, "other": 1000 } ] }
const {data} = state;
let res = {
...state,
data:[
data[0],
{...data[1],val:data[1].val+ 1},
...data.slice(2)
]
}
console.log(result)
You could assign parts of the array/objects.
var object = { data: [{ val: 1, other: 10 }, { val: 10, other: 100 }, { val: 100, other: 1000 }] },
result = {
...object,
data: Object.assign(
[...object.data],
{
1: Object.assign(
{},
object.data[1],
{ val: object.data[1].val + 1 }
)
}
)
};
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
With a bit help of some functional helpers that is actually quite elegant:
const mapOne = (index, mapper) => array => array.map((it, i) => i === index ? mapper(it) : it);
const lens = (key, mapper) => obj => ({ ...obj, [key]: mapper(obj[key]) });
// somewhere
this.setState(mapOne(1, lens("val", it => it + 1)));