I was looking for group by functionality over array of array and wrote something like,
var response = {
"data": {
"results": {
"facebook": [
[
"Campaign Name",
'promo',
'id'
],
[
"Sok_MPA_10Link_IntOnlineShoppingSites_1412",
"promo1",
4
],
[
"PPLA",
"promo1",
9
],
[
"PPLA",
"promo2",
90
],
[
"Sok_MPA_10Link_IntOnlineShoppingSites_1412",
"promo1",
45,
5
]
]
}
},
"exception": null,
"status": "OK",
"sokratiRequestId": null,
"json": null
};
var mapData = {};
$.map(response.data.results, function(value, key) {
headers = value.shift();
$.map(response.data.results[key], function(data) {
$.each(headers, function(index, column) {
if (index == headers.indexOf('id')) return true;
if (mapData[column] == undefined) {
mapData[column] = {};
}
if (mapData[column][data[index]] == undefined) {
mapData[column][data[index]] = 0;
}
mapData[column][data[index]] += data[headers.indexOf('id')]
});
});
console.log(mapData)
});
Basically I want to group over multiple fields in the array - Like ["Campaign Name" & promo']
Any good way to achieve it?
Output should be -
"Campaign Name": {
PPLA: 99
Sok_MPA_10Link_IntOnlineShoppingSites_1412: 49
},
"promo": {
promo1: 58
promo2: 90
}
https://jsfiddle.net/ezg8Lzdr/
Note
Answer should purely be in JS - No other frameworks [No lodash]
Edit:
With Array.prototype.reduce() and an object for the result. The given groups items are taken either for the group property and their index for the wanted data part to get a sub property.
var response = { "data": { "results": { "facebook": [["Campaign Name", 'promo', 'id'], ["Sok_MPA_10Link_IntOnlineShoppingSites_1412", "promo1", 4], ["PPLA", "promo1", 9], ["PPLA", "promo2", 90], ["Sok_MPA_10Link_IntOnlineShoppingSites_1412", "promo1", 45, 5]] } }, "exception": null, "status": "OK", "sokratiRequestId": null, "json": null },
data = response.data.results.facebook,
result = {},
groups = data.shift();
groups.pop();
result = data.reduce(function (r, a) {
groups.forEach(function (b, i) {
r[b] = r[b] || {};
r[b][a[i]] = (r[b][a[i]] || 0) + a[2];
});
return r;
}, {});
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
I wrote a small function on JS that might help you:
function groupBy(collection, property) {
var i = 0, val, index,
values = [], result = [];
for (; i < collection.length; i++) {
val = collection[i][property];
index = values.indexOf(val);
if (index > -1)
result[index].push(collection[i]);
else {
values.push(val);
result.push([collection[i]]);
}
}
return result;
};
The use is simple:
groupBy(myArray, "facebook");
If the data schema is pretty much fixed:
var response = {
"data": {
"results": {
"facebook": [
[
"Campaign Name",
'promo',
'id'
],
[
"Sok_MPA_10Link_IntOnlineShoppingSites_1412",
"promo1",
4
],
[
"PPLA",
"promo1",
9
],
[
"PPLA",
"promo2",
90
],
[
"Sok_MPA_10Link_IntOnlineShoppingSites_1412",
"promo1",
45,
5
]
]
}
},
"exception": null,
"status": "OK",
"sokratiRequestId": null,
"json": null
};
var face = response.data.results.facebook;
//remove first row which is a schema
face.shift();
//take in index of column, return grouped sum
var reduce = function(index) {
return face.reduce((t, a) => {
t[a[index]] = (t[a[index]] || 0) + a[2];
return t;
}, {});
}
//final result
var result = {
"Campaign Name": reduce(0),
"promo": reduce(1)
}
document.write('<pre>' + JSON.stringify(result, 0, 4) + '</pre>');
For such basic operations on arrays, collection or whatever in JavaScript, I would recommend to use frameworks like lodash which is full of tricks like this one you will need everywhere in your project:
https://lodash.com/docs#groupBy
The most common way is to use library to data manipulation like https://lodash.com/
Using this library you can group your data like this:
var result = _.chain(data)
.groupBy("color")
.pairs()
.map(function(currentItem) {
return _.object(_.zip(["color", "users"], currentItem));
})
.value();
Related
Is it possible to filter an array of objects by multiple values?
E.g in the sample below can I filter it by the term_ids 5 and 6 and type car at the same time?
[
{
"id":1,
"term_id":5,
"type":"car"
},
{
"id":2,
"term_id":3,
"type":"bike"
},
{
"id":3,
"term_id":6,
"type":"car"
}
]
Definitely up for using a library if it makes it easier.
You can do it with Array.filter
var data = [{
"id": 1,
"term_id": 5,
"type": "car"
},
{
"id": 2,
"term_id": 3,
"type": "bike"
},
{
"id": 3,
"term_id": 6,
"type": "car"
}
];
var result = data.filter(function(v, i) {
return ((v["term_id"] == 5 || v["term_id"] == 6) && v.type == "car");
})
console.log(result)
The following function will help you out.
nestedFilter = (targetArray, filters) => {
var filterKeys = Object.keys(filters);
return targetArray.filter(function (eachObj) {
return filterKeys.every(function (eachKey) {
if (!filters[eachKey].length) {
return true;
}
return filters[eachKey].includes(eachObj[eachKey]);
});
});
};
Use this function with filters described as below:
var filters = {
"id": ["3"],
"term_id": ["6"],
"type": ["car","bike"]
}
Dont pass empty array. If there are no values in the array, skip that property in the filters.
The result will be filtered array.
You can do this with plain js filter() method and use && to test for both conditions.
var data = [{"id":1,"term_id":5,"type":"car"},{"id":2,"term_id":3,"type":"bike"},{"id":3,"term_id":6,"type":"car"}];
var result = data.filter(function(e) {
return [5, 6].includes(e.term_id) && e.type == 'car'
});
console.log(result);
Another way to do it is to use lodash filter + reduce.
const arr = [{"id":1,"term_id":5,"type":"car"},{"id":2,"term_id":3,"type":"bike"},{"id":3,"term_id":6,"type":"car"}];
const result = [
{term_id: 5, type: 'car'},
{term_id: 6, type: 'car'},
].reduce((prev, orCondition) => prev.concat(_.filter(arr, orCondition)), []);
console.log(result);
<script src="https://cdn.jsdelivr.net/npm/lodash#4.17.21/lodash.min.js"></script>
I tried to create new array object from array , set rank according to its value . If value is the same, set the same rank and if next value is different set rank by skipping same rank length .
Expected result is
[
{
"rank": 1,
"data": 45
},
{
"rank": 2,
"data": 33
},
{
"rank": 3,
"data": 8
},
{
"rank": 4,
"data": 5
},
{
"rank": 4,
"data": 5
},
{
"rank": 6,
"data": 2
}
]
var data = [8,5,2,33,5,45];
var rankList = [];
var uniqueList = [];
var rank = 0;
var sameRank = 0;
data.sort(function(a,b) { return b - a; });
for(var i in data) {
if(uniqueList.includes(data[i])) {
rank++;
rankList.push({rank: sameRank, data: data[i]});
continue;
}
rank++;
sameRank++;
rankList.push({rank: rank, data: data[i]});
}
console.log(rankList);
Once you've sorted the array, create another array of objects with .map, keeping track of the last rank and data used. If the new data is the same, use the same rank (the one taken from a prior iteration) - otherwise, use the current iteration index plus 1:
const data = [8, 5, 2, 33, 5, 45];
data.sort((a, b) => b - a);
let lastRank = 0;
let lastData;
const output = data.map((data, i) => {
const objToReturn = { data };
objToReturn.rank = data === lastData ? lastRank : i + 1;
lastData = data;
lastRank = objToReturn.rank;
return objToReturn;
});
console.log(output);
Maybe this question has already been asked and answered somewhere but after searching for more than 3 hrs I'm asking this question.
Below is my JSON data
var my_data = [
{
"TempRture_qc": 4,
"VoltAGE": 44.09722,
"TempRture": 22.32,
"VoltAGE_qc": 55,
"_time": "2018-08-07T03:39:29.001Z"
},
{
"TempRture_qc": 2,
"VoltAGE": 42.09722,
"TempRture": 22.12,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:30.006Z"
},
{
"TempRture_qc": 1,
"VoltAGE": 43.09722,
"TempRture": 22.82,
"VoltAGE_qc": 0,
"_time": "2018-08-07T03:39:31.009Z"
}
];
desired output i need
[
{
"name": "TempRture_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":4},
{"name":"2018-08-07T03:39:30.006Z","y":2},
{"name":"2018-08-07T03:39:33.017Z","y":1}
]
},
{
"name": "VoltAGE",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":44.09722},
{"name":"2018-08-07T03:39:30.006Z","y":42.09722},
{"name":"2018-08-07T03:39:33.017Z","y":43.09722}
]
},
{
"name": "TempRture",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":22.32},
{"name":"2018-08-07T03:39:30.006Z","y":22.12},
{"name":"2018-08-07T03:39:33.017Z","y":22.82}
]
},
{
"name": "VoltAGE_qc",
"data": [
{"name":"2018-08-07T03:39:29.001Z","y":55},
{"name":"2018-08-07T03:39:30.006Z","y":0},
{"name":"2018-08-07T03:39:33.017Z","y":0}
]
}
]
for getting this above output i have tried below code.
var accounting = [];
var fieldName = {};
for (var x in obj){
var mykey = Object.keys(obj[x]);
for (var mk in mykey){
if(mykey[mk]=='VoltAGE'){
fieldName.name = mykey[mk];
// accounting.push({
// "name":mykey[mk]
// })
}
if(mykey[mk]=='TempRture'){
fieldName.name = mykey[mk];
}
// console.log(mykey[mk]); //to get the key name
}
accounting.push({
"name" : obj[x]._time,
"y" : obj[x][employees.name],
})
fieldName.data = accounting;
}
console.log(fieldName );
by doing this what I'm getting is below JSON
{ name: 'TempRture',
data:
[ { name: '2018-08-07T03:39:29.001Z', y: 22.32 },
{ name: '2018-08-07T03:39:32.014Z', y: 22.12 },
{ name: '2018-08-07T03:39:33.017Z', y: 22.82 } ] }
I'm not able to understand how I will get the data in one JSON object.
For a solution with low time complexity, try .reduceing into an object indexed by keys of the inner object, creating a { name, data: [] } at that key in the accumulator if it doesn't exist there yet. Then, push to the data array, and get the values of the whole object:
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
console.log(Object.values(
my_data.reduce((a, { _time, ...obj }) => {
Object.entries(obj).forEach(([name, val]) => {
if (!a[name]) a[name] = { name, data: [] };
a[name].data.push({ name: _time, y: val });
});
return a;
}, {})
));
var my_data=[{"TempRture_qc":4,"VoltAGE":44.09722,"TempRture":22.32,"VoltAGE_qc":55,"_time":"2018-08-07T03:39:29.001Z"},{"TempRture_qc":2,"VoltAGE":42.09722,"TempRture":22.12,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:30.006Z"},{"TempRture_qc":1,"VoltAGE":43.09722,"TempRture":22.82,"VoltAGE_qc":0,"_time":"2018-08-07T03:39:31.009Z"}]
var keys = Object.keys(my_data[0])
var result= [];
for(i = 0; i<keys.length-1; i++) {
var obj = {name: keys[i],data: []}
obj.data = my_data.map(val=>({name: val["_time"], y: val[keys[i]]}));
result.push(obj);
}
console.log(result)
An understandable answer with map, findIndex and forEach functions will be
var my_data = [{ "TempRture_qc": 4, "VoltAGE": 44.09722, "TempRture": 22.32, "VoltAGE_qc": 55, "_time": "2018-08-07T03:39:29.001Z" }, { "TempRture_qc": 2, "VoltAGE": 42.09722, "TempRture": 22.12, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:30.006Z" }, { "TempRture_qc": 1, "VoltAGE": 43.09722, "TempRture": 22.82, "VoltAGE_qc": 0, "_time": "2018-08-07T03:39:31.009Z" } ],
result = [];
my_data.map(itm => {
let keys = Object.keys(itm);
keys.forEach(iitt => {
if (iitt != '_time') {
let index = result.findIndex(ii => {
return ii.name == iitt;
})
if (index == -1) {
result.push({
name: iitt,
data: []
});
result[result.length - 1].data.push({
name: itm["_time"],
y: itm[iitt]
})
} else {
result[index].data.push({
name: itm["_time"],
y: itm[iitt]
});
}
}
})
})
console.log(result)
I have few arrays of JSON objects.I need to iterate over the arrays and return true if there are two or more elements with the same userId value.
[{
"name":"John",
"age":30,
"userId": 5,
}],
[{
"name":"Benjamin",
"age":17,
"userId": 5,
}],
[{
"name":"Johnatan",
"age":35,
"userId": 10,
}]
Here is my method so far, I'm iterating over the array and checking is there a user with 506 userId presence.
isPostedMultiple = (data) => {
for (let j = 0; j < data.length; j++) {
if (data[j].UserId == '506') {
console.log('506 data :', data[j]);
} else {
console.log('not 506 data');
}
}
}
First of all the Object you have given is erroneous. Make it correct. Coming to the problem,
You can use a combination of Array.prototype.some and Array.prototype.filter.
data.some(
(el, i, arr) => arr.filter(_el => _el.userId == el.userId).length > 1
);
To check if there exists more than one element matching certain condition.
var data = [{
"name": "John",
"age": 30,
"userId": 5,
},
{
"name": "Benjamin",
"age": 17,
"userId": 5,
},
{
"name": "Johnatan",
"age": 35,
"userId": 10,
}
];
var result = data.some(
(el, i, arr) => arr.filter(_el => _el.userId == el.userId).length > 1
);
console.log(result)
You can merge arrays using array spread syntax and than use the reduce with the filter method
const mergedArrays = [...arr1, ...arr2, ...arr3];
const isDublicated = mergedArrays.reduce(
(acc, item) => acc || mergedArrays.filter(user => user.userId === item.userId) > 1,
false
);
To achieve expected result, use below option of using filter and findIndex to iterate over every array and compare userId
var x = [[{
"name":"John",
"age":30,
"userId": 5,
}],
[{
"name":"Benjamin",
"age":17,
"userId": 5,
}],
[{
"name":"Johnatan",
"age":35,
"userId": 10,
}]]
x = x.filter((v, i, self) =>
i === self.findIndex((y) => (
y[0].userId === v[0].userId
))
)
console.log(x);
code sample - https://codepen.io/nagasai/pen/wmWqdY?editors=1011
var jsonObj1 = [{
"name":"John",
"age":30,
"userId": 5
},
{
"name":"Benjamin",
"age":17,
"userId": 5
},
{
"name":"Johnatan",
"age":35,
"userId": 10
}];
var jsonObj2 = [{
"name":"John",
"age":30,
"userId": 5
},
{
"name":"Benjamin",
"age":17,
"userId": 15
},
{
"name":"Johnatan",
"age":35,
"userId": 10
}];
var logger = document.getElementById('logger');
logger.innerHTML = "";
function checkForDupIds(jsonObj, headerStr) {
var logger = document.getElementById('logger');
var hasDups = [];
var items = [];
for(var a=0;a<jsonObj.length;a++) {
if (items.includes(jsonObj[a].userId)) {
hasDups.push(jsonObj[a].userId);
} else {
items.push(jsonObj[a].userId);
}
}
logger.innerHTML += "<h1>" + headerStr + "</h1>";
for(var b=0;b<hasDups.length;b++) {
logger.innerHTML += "<div>" + hasDups[b] + "</div>\n";
console.log(hasDups[b]);
}
if (hasDups.length === 0) {
logger.innerHTML += "<div>No Duplicates Found</div>\n";
}
}
checkForDupIds(jsonObj1, "jsonObj1");
checkForDupIds(jsonObj2, "jsonObj2");
<html>
<body>
<div id='logger'></div>
</body>
</html>
You can loop over the array and keep a count of how many times each userId value appears. If you get to 2 for any value, stop and return false (or some other suitable value).
Array.prototype.some allows looping over the array until the condition is true, so it only loops over the source once. The data in the OP was invalid, I've modified it to be an array of objects.
var data = [{
"name":"John",
"age":30,
"userId": 5
},
{
"name":"Benjamin",
"age":17,
"userId": 5
},
{
"name":"Johnatan",
"age":35,
"userId": 10
}]
function hasDupIDs(data) {
// Store for userId values
var ids = {};
// Loop over values until condition returns true
return data.some(function(x) {
// If haven't seen this id before, add to ids
if (!ids.hasOwnProperty(x.userId)) ids[x.userId] = 0;
// Increment count
ids[x.userId]++;
// Return true if second instance
return ids[x.userId] > 1;
});
}
console.log(hasDupIDs(data));
If you want more concise code, you can use:
var data = [
{"name":"John","age":30,"userId": 5},
{"name":"Benjamin","age":17,"userId": 5},
{"name":"Johnatan","age":35,"userId": 10}];
function hasDupIDs(data) {
var ids = {};
return data.some(x => {
ids[x.userId] || (ids[x.userId] = 0);
return ++ids[x.userId] > 1;
});
}
console.log(hasDupIDs(data));
I have an array like bellow each index contains different set of objects,I want to create an uniformal data where object missing in each index will with Value:0 ,
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
]
];
how can I get an array like bellow using above above array
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
{axis:"Sending Money",value:0,id:6},
{axis:"Other",value:0,id:7},
],
[
{axis:"Email",value:0,id:1},
{axis:"Social Networks",value:0,id:2},
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
]
];
There are two functions:
getAllEntries that find all objects and stores them into a variable accEntries. Then accEntries is used to search for all occurrences in a sub-array of d. This whole process is done in checkArray.
checkArray is used to fetch all found and not-found entries in d. Both Arrays (found and not-found) are then used to build a new sub-array that contains either found entries with certain values and/or not-found entries with values of 0.
Hope this helps:
var d = [
[
{
axis: 'Email',
value: 59,
id: 1
},
{
axis: 'Social Networks',
value: 56,
id: 2
},
],
[
{
axis: 'Sending Money',
value: 18,
id: 6
},
{
axis: 'Other',
value: 15,
id: 7
},
]
];
function getAllEntries(array) {
var uniqueEntries = [];
array.forEach(function (subarray) {
subarray.forEach(function (obj) {
if (uniqueEntries.indexOf(obj) === - 1) uniqueEntries.push(obj);
});
});
return uniqueEntries;
}
function checkArray(array, acceptedEntries) {
var result = [];
array.forEach(function (subArray) {
var subResult = [];
var foundEntries = [];
subArray.forEach(function (obj) {
if (foundEntries.indexOf(obj.axis) === - 1) foundEntries.push(obj.axis);
});
var notFound = acceptedEntries.filter(function (accepted) {
return foundEntries.indexOf(accepted.axis) === - 1;
});
foundEntries.forEach(function (found) {
subArray.forEach(function (obj) {
if (obj.axis === found) subResult.push(obj);
});
});
notFound.forEach(function (notfound, index) {
subResult.push({
axis: notfound.axis,
value: 0,
id: notfound.id
});
});
result.push(subResult);
});
return result;
}
var accEntries = getAllEntries(d);
var result = checkArray(d, accEntries);
console.log(result);
You can loop over the array to find all the unique objects and then again loop over to push the values that are not present comparing with the array of objects of unique keys.
You can use ES6 syntax to find if an object with an attribute is present like uniKeys.findIndex(obj => obj.axis === val.axis); and the to push with a zero value use the spread syntax like d[index].push({...val, value: 0});
Below is the snippet for the implementation
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2},
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7},
{axis:"Social Networks",value:89,id:2},
]
];
var uniKeys = [];
$.each(d, function(index, item) {
$.each(item, function(idx, val){
const pos = uniKeys.findIndex(obj => obj.axis === val.axis);
if(pos == - 1) {
uniKeys.push(val);
}
})
})
$.each(d, function(index, item) {
var temp = [];
$.each(uniKeys, function(idx, val){
const pos = item.findIndex(obj => obj.axis === val.axis);
if(pos == - 1) {
d[index].push({...val, value: 0});
}
})
})
console.log(d);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
How about a short shallowCopy function (Object.assign is not available in IE) and otherwise less than 10 new lines of code?
var d = [
[
{axis:"Email",value:59,id:1},
{axis:"Social Networks",value:56,id:2}
],
[
{axis:"Sending Money",value:18,id:6},
{axis:"Other",value:15,id:7}
]
];
var newD_0 = [shallowCopy(d[0][0]), shallowCopy(d[0][1]), shallowCopy(d[1][0]), shallowCopy(d[1][1])];
var newD_1 = [shallowCopy(d[0][0]), shallowCopy(d[0][1]), shallowCopy(d[1][0]), shallowCopy(d[1][1])];
newD_0[2].id = 0;
newD_0[3].id = 0;
newD_1[0].id = 0;
newD_1[1].id = 0;
d = [newD_0, newD_1];
function shallowCopy(obj) {
var copy = {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = obj[key];
}
}
return copy;
}
console.log(JSON.stringify(d));
RESULT:
[
[
{
"axis":"Email",
"value":59,
"id":1
},
{
"axis":"Social Networks",
"value":56,
"id":2
},
{
"axis":"Sending Money",
"value":18,
"id":0
},
{
"axis":"Other",
"value":15,
"id":0
}
],
[
{
"axis":"Email",
"value":59,
"id":0
},
{
"axis":"Social Networks",
"value":56,
"id":0
},
{
"axis":"Sending Money",
"value":18,
"id":6
},
{
"axis":"Other",
"value":15,
"id":7
}
]
]