How to calculate the data in JSON? - javascript

The essence is that I have json file:
[
{
"id": 0,
"username": "Antony",
"users": [
{
"id": 1,
"like": 0
},
{
"id": 2,
"like": 1
},
{
"id": 3,
"like": 0
},
{
"id": 4,
"like": 1
}
]
},
{
"id": 1,
"username": "Janet",
"users": [
{
"id": 0,
"like": 0
},
{
"id": 2,
"like": 1
},
{
"id": 3,
"like": 1
},
{
"id": 4,
"like": 1
}
]
},.......
I need to count how many "likes", have each user.
ie:
For example, take the first id == 0.
We pass on the object, which can be very much and look:
If id == 0 and like == 1, add 1 to the array.
In the end, I must have:
usersWithLikes [id User] = number of likes for all objects
For example:
usersWithLikes [0] = 3
usersWithLikes [1] = 1
usersWithLikes [2] = 4
usersWithLikes [3] = 0
At the moment, I think so:
thumbsUp_data - json data
var usersWithLikes = thumbsUp_data.map(function(user_data){
return user_data.users.filter(function(value){
return value.like == 1;
}).length;
});
But this is not correct, because it considers how many likes the object.
Help with the decision ...

Filter out the user object, grab the first element of the returned array and then filter on that object's user array for like === 1 returning it's length;
function howManyLikes(id) {
return arr.filter(function (user) {
return user.id === id;
})[0].users.filter(function (el) {
return el.like === 1;
}).length;
}
howManyLikes(1); // 3
DEMO

thumbsUp_data.forEach(function(data) {
data.users.forEach(function(value) {
usersWithLikes[value.id] = usersWithLikes[value.id] || 0;
usersWithLikes[value.id] += value.like;
});
});
Thats all, it's a solution!

Related

Javascript apply sort(ascending order) for array of objects using key value

I have tried to sort the javascript array which is having "order" as the key but it is sorting only the parent array and inner objects are not sorting
Here is my sample code which have tried,
Sample JSON response:
var MainObj = [
{
"title": "Merchant2",
"order": 2,
"subMenu": [
{
"subMenu1": "Initiate2",
"order": 2
},
{
"subMenu2": "Initiate1",
"order": 1
}
]
},
{
"title": "Merchant1",
"order": 1,
"subMenu": [
{
"subMenu1": "Initiate2",
"order": 2
},
{
"subMenu2": "Initiate1",
"order": 1
}
]
}
]
And below is the sort functionality,
var MainObj = [{
"title": "Merchant2",
"order": 2,
"subMenu": [{
"subMenu1": "Initiate2",
"order": 2
},
{
"subMenu2": "Initiate1",
"order": 1
}
]
},
{
"title": "Merchant1",
"order": 1,
"subMenu": [{
"subMenu1": "Initiate2",
"order": 2
},
{
"subMenu2": "Initiate1",
"order": 1
}
]
}
]
var sort = function(prop, arr) {
prop = prop.split('.');
var len = prop.length;
arr.sort(function(a, b) {
var i = 0;
while (i < len) {
a = a[prop[i]];
b = b[prop[i]];
i++;
}
if (a < b) {
return -1;
} else if (a > b) {
return 1;
} else {
return 0;
}
});
return arr;
};
console.log(sort('order', MainObj));
But the expected output should be in the below way,
[
{
"title": "Merchant",
"order": 1,
"subMenu": [
{
"subMenu1": "Initiate1",
"order": 1
},
{
"subMenu1": "Initiate2",
"order": 2
},
]
}
]
You're not reaching into subMenu at all in your sort function. It's also unclear why you're splitting prop, and what the length has to do with anything, but based on your sample input and expected output, it can be much simpler:
// sort an array of objects based on the value of those objects' `prop`
// creates a new array with [...] because `sort` mutates. this could be changed
// to use partial application if you'll have the same logic for other properties
// in the future.
const sortByProp = (prop, xs) =>
[...xs.sort((a, b) => a[prop] - b[prop])]
// sort the parent array based on some prop, and subMenu's array based on
// the same prop. if `subMenu` can possibly change down the line, that should be
// moved to an argument as well.
const sort = (prop, xs) =>
sortByProp(prop, xs.map((x) => ({
...x,
subMenu: sortByProp(prop, x.subMenu)
})))
// test
console.log(
JSON.stringify(
sort('order', testInput), null, 2
)
)

JavaScript: check if duplicate key values exist in array of objects, if there are, rename it

I have an array "source"
source : [
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}
]
I want to rename the secondId when there are duplicate like this:
[
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1_2
},
{
"id": 3,
"secondId": 1_3
}
]
I have this so far:
for (i = 0 ; i < source.length ; i++) {
for (j = 0 ; j < source.length ; j++){
if (source[i]["id"] != source[j]["id"] && source[i]["secondId"] === source[j]["secondId"]){
source[j]["secondId"] += "_" + (i+1);
}
console.log(source[j]["secondId"]);
}
}
and I'm getting:
[
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1_2
},
{
"id": 3,
"secondId": 1_2_3
}
]
I tried to use some:
if(source[j]["secondId"].includes("_"+ (i+1))){
console.log(source[j]["secondId"].split("_"+ (i+1)).shift());
}
but I'm getting:
"secondId": 1
"secondId": 1
"secondId": 1_2
How can I do it? Any tips please?
A version using Array.reduce:
let source = [
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}];
let output = Object.values(
source.reduce((a, e, i) => {
let testId = e.secondId.toString();
if (a[testId]) {
testId = testId.split("_")[0] + "_" + (i + 1);
}
a[testId] = {...e, secondId: testId};
return a;
}, {})
);
console.log(output);
This may be a solution to achieve the (assumed) desired objective:
Code Snippet
const markDupes = arr => (
arr.reduce(
(fin, {id, secondId}) => ({
tm: {
[secondId]: (fin.tm[secondId] || 0) + 1
},
newArr: [
...fin.newArr,
{id, secondId: secondId.toString() + (
fin.tm[secondId] > 0 ? `_${fin.tm[secondId]+1}` : ''
)}
]
}),
{ tm: {}, newArr: [] }
)?.newArr
);
const source = [{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}
];
console.log(markDupes(source));
Explanation
Use .reduce to iterate over the array of objects
Set up an initial fin object with two props tm (tracking-map) and newArr (the result-array which will have the secondId updated as desired)
For each object, destructure to directly access id and secondId
Update the map tm based on the secondId with a counter
Append to the newArr an object with id and secondId props with the latter (secondId) being converted to a string to store values of the format 1_2, 1_3, etc
NOTE: This may not be an optimal solution.
When you convert 1 to 1_2 or 1_3 it is converting a number to a string which will be a huge pain when you have a use for the number later. Instead what i have done is convert that number to a decimal for as 1.2 ,1.3 which means you can do all sorts of computation on a number without much conversion
let source = [
{
"id": 1,
"secondId": 1
},
{
"id": 2,
"secondId": 1
},
{
"id": 3,
"secondId": 1
}
];
let val = {};
for (const i in source) {
let v = source[i].secondId
val[v] = val[v] ? val[v] : v
if (val[v] !== 1) {
console.log(source[i].id, v);
source[i].secondId = Number(v.toFixed(2)) + (val[v] * 0.1)
}
val[v]++
}
console.log(source);
if you are really kean of using string instead use source[i].secondId = v+'_'+val[v] instead inside the if by changing the original source json object as well

Sort array of object on a property but keep the objects first for which property is missing

Here is an example
testWidgetOrderSort = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
]
Here for the the object { "_id": "oh I would be very first" } does not have the property order so it should come first.
And then the rest of the objects should be sorted according to the property "order"
So after sorting it should be,
output= [ { _id: 'oh I would be very first' },
{ _id: 'my', order: 0 },
{ _id: 'name', order: 1 },
{ _id: 'is', order: 2 },
{ _id: 'adam', order: 3 } ]
Logic is basic array sorting logic.
If both a.order and b.order are defined return 1 or -1 depending on the largest value.
If either one of them is undefined return 1 or -1 depending on the defined value.
Please Note: The value 1 and -1 determines the relative position between the two nodes. Returning 1 places a after b and -1 places a before b.
const testWidgetOrderSort = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
];
const output = testWidgetOrderSort.sort((a, b) => {
if( a.order !== undefined && b.order !== undefined ) {
return a.order > b.order ? 1 : -1;
} else {
return a.order !== undefined ? 1 : -1
}
});
console.log(output);
I came up with something like this:
const test = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
];
const x = test.sort((a, b) => {
const [STAY, SWAP] = [-1, 1];
if (!a.hasOwnProperty('order')) { return STAY; }
if (!b.hasOwnProperty('order')) { return SWAP; }
return a.order - b.order;
});
console.log(x);
You just have to pass the custom comparator function
if (!("order" in a)) return -1;
if (!("order" in b)) return 1;
else return a.order - b.order;
1) return -1 if property order doesn't exist in a.
2) return 1 if property order doesn't exist in b.
3) if both the object has order property then just sort in ascending order.
const arr = [
{ _id: "name", order: 1 },
{ _id: "is", order: 2 },
{ _id: "my", order: 0 },
{ _id: "oh I would be very first" },
{ _id: "adam", order: 3 },
];
const result = arr.sort((a, b) => {
if (!("order" in a)) return -1;
if (!("order" in b)) return 1;
else return a.order - b.order;
});
console.log(result);
If you don't care about the performance too much, the below should be fine,
const testWidgetOrderSort = [
{ "_id": "name", "order": 1 },
{ "_id": "is", "order": 2 },
{ "_id": "my", "order": 0 },
{ "_id": "oh I would be very first" },
{ "_id": "adam", "order": 3 }
];
const finalArr = testWidgetOrderSort.filter(a => typeof a.order === "undefined");
const sortedArrWithOrderItems = testWidgetOrderSort.filter(a => typeof a.order !== "undefined").sort((a,b) => (a.order > b.order ? 1 : -1));
finalArr.push(...sortedArrWithOrderItems);
console.log(finalArr);
Note: Personally I would recommend going with #Nitheesh or #decpk solution, it is more clean and performance wise better. My solution is just to give another solution for the problem

How can I sum values inside reduce with multi condition?

I want to sum up all of data base on two condition in side my reduce function.
Let's say I have data as following:
const data = [
{
"id": 1,
"option": BP,
"result": 'win',
"amount": 50
},
{
"id": 3,
"option": BP,
"result": 'win',
"amount": 20
},
{
"id": 5,
"option":VN,
"result": 'win',
"amount": 50
},
{
"id": 5,
"option":GB,
"result": 'loss',
"amount": 40
}
];
Here is my code:
data.reduce((newValueBetting, modelBetting) => {
if (
modelBetting.option === 'VN'
&& modelBetting.result === 'win'
) {
newValueBetting += modelBetting.amount;
}
return newValueBetting;
}, 0);
Regarding to this code. it will sum when my data is matches with condition. But, if I want to sum up option === 'BP' && result === 'win'. So, I don't want to write code again. Any idea? How can I make my reduce run only one time and get to this object:
{
TotalBPWin: 70,
TotalVN: 50,
TotalGBLoss: 40
}
While using reduce pass an object with all 3 key TotalBPWin, TotalVN, TotalGBLoss with initial value as 0. Then conditionally add them together.
const data = [{
id: 1,
option: "BP",
result: "win",
amount: 50,
},
{
id: 3,
option: "BP",
result: "win",
amount: 20,
},
{
id: 5,
option: "VN",
result: "win",
amount: 50,
},
{
id: 5,
option: "GB",
result: "loss",
amount: 40,
},
];
const accumulator = {
TotalBPWin: 0,
TotalVN: 0,
TotalGBLoss: 0,
};
const result = data.reduce((newValueBetting, { option, result, amount }) => {
if (option === "VN" && result === "win") {
newValueBetting["TotalVN"] += amount;
} else if (option === "BP" && result === "win") {
newValueBetting["TotalBPWin"] += amount;
} else if (option === "GB" && result === "loss") {
newValueBetting["TotalGBLoss"] += amount;
}
return newValueBetting;
}, accumulator);
console.log(result);
The idea is to use object in reduce rather than 0. The following is the simple implementation, you will probably need to refine the condition in the reduce for summation.
const data = [
{
"id": 1,
"option": 'BP',
"result": 'win',
"amount": 50
},
{
"id": 3,
"option": 'BP',
"result": 'win',
"amount": 20
},
{
"id": 5,
"option":'VN',
"result": 'win',
"amount": 50
},
{
"id": 5,
"option":'GB',
"result": 'loss',
"amount": 40
}
];
let result = {
'VN':0,
'GB':0,
'BP':0
};
data.reduce((acc,item) => {
result[item.option] += item.amount
return acc;
}, result);
console.log(result)
The following reduce will get you a sum of counts for each option. Importantly, if any option has zero wins, then there won't be an associated sum for it (See the log output).
const data = [{id:1,option:"BP",result:"win",amount:50},{id:3,option:"BP",result:"win",amount:20},{id:5,option:"VN",result:"win",amount:50},{id:5,option:"GB",result:"loss",amount:40}];
const result = data.reduce((all, el) => {
if (el.result === "win") {
all[el.option] = (all[el.option] || 0) + el.amount;
}
return all;
}, {});
console.log(result.BP);
console.log(result.VN);
console.log(result.GB);
You just need a simple loop for this and no need for if() conditionals
const data=[{id:1,option:"BP",result:"win",amount:50},{id:3,option:"BP",result:"win",amount:20},{id:5,option:"VN",result:"win",amount:50},{id:5,option:"GB",result:"loss",amount:40}];
const res= {};
data.forEach(o=>{
const k = 'Total' + o.option + o.result
res[k] = (res[k] || 0) + o.amount
});
console.log(res)

Removing an object from an array if it exists in the array already (Otherwise add it)

I have a set of checkboxes - which the user ticks on. The checkboxes pass some data an id and a name that i need later on for sorting. Because two objects are not equal even though they contain the same values I cant use Array.includes.
Here is an example of the data
[
{
"id": 9,
"name": "age_group_ids"
},
{
"id": 9,
"name": "age_group_ids"
},
{
"id": 9,
"name": "earnings_group_ids"
},
{
"id": 3,
"name": "earnings_group_ids"
},
]
This is the current function (which would work if the items were not objects
const submitFilterDetails = (value) => {
return async (dispatch,getState) => {
const currentArray = (getState().filter.filtersArray);
if(!currentArray.includes(value)) {
dispatch(addToFiltersArray(value))
} else {
dispatch(removeFromFiltersArray(value));
}
}
}
How can you sort this so I only have unique values
You can use find :
YourArray.find(obj => obj.id == value.id && obj.name == value.name);
const src = [
{
"id": 9,
"name": "age_group_ids"
},
{
"id": 9,
"name": "age_group_ids"
},
{
"id": 1,
"name": "earnings_group_ids"
},
]
const out = src.reduce((acc, curr) => acc.some(i => i.id === curr.id) ? acc : acc.concat([curr]) , [])
// the `out` variable has 2 unique items

Categories