Linq.Js Group By with Count - javascript

I have the following array:
var data= [{ "Id": 1, "Name": "NameOne"}
{ "Id": 2, "Name": "NameTwo"}
{ "Id": 2, "Name": "NameTwo"}]
{ "Id": 3, "Name": "NameThree"}]
Using linq.js I would like to return the following array:
var data= [{ "Id": 1, "Name": "NameOne", Total: 1}
{ "Id": 2, "Name": "NameTwo", Total: 2}
{ "Id": 3, "Name": "NameThree", Total: 1}]
This means that I need to use GroupBy() with a Count(). I am not sure how to apply this using the linq.js reference.

It's simple really:
var data = [
{ Id: 1, Name: 'NameOne' },
{ Id: 2, Name: 'NameTwo' },
{ Id: 2, Name: 'NameTwo' },
{ Id: 3, Name: 'NameThree' }
];
var query = Enumerable.From(data)
// GroupBy (keySelector, elementSelector, resultSelector, compareSelector)
.GroupBy(
null, // (identity)
null, // (identity)
"{ Id: $.Id, Name: $.Name, Total: $$.Count() }",
"'' + $.Id + '-' + $.Name"
)
.ToArray();
Use the overload of GroupBy() that includes the resultSelector, you'll want to grab the count of the grouped items (the second parameter).

You were probably having issues with the data not being uniform. a reduce flattens your data structure, and then you can manipulate it as you wish in the .Select().
var intialData = [[{ "Id": 1, "Name": "NameOne"}, { "Id": 2, "Name": "NameTwo"}, { "Id": 2, "Name": "NameTwo"}], { "Id": 3, "Name": "NameThree"}];
var data = Enumerable.From(intialData.reduce(function(a,b) { return a.concat(b); }))
.GroupBy(function(item) { return item.Id; })
.Select(function(item) { return {"Id":item.source[0].Id, "Name":item.source[0].Name, "Total": item.source.length}; })
.ToArray();

Related

Filter two arrays data based on nested ids

I have two arrays as follow:
const categories = [
{"itemId": [1], "menuItemCateName": "Popular"},
{"itemId": [1, 2], "menuItemCateName": "Featured"}
]
const items = [
{
"Id": 1,
"price": 10,
"itemName": "Spicy burger"
},
{
"Id": 2,
"price": 10,
"itemName": "Pizza"
}
]
I want to filter the data based on itemId inside array categories so the result will looks like
[
{ name: 'Popular', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"} ] },
{ name: 'Featured', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"},{ "Id": 2, "price": 10, "itemName": "Pizza"} ] }
]
What i tried is:
let data = []
categories.map(category => {
items.filter(item => item.Id === category.itemId[0]).map(b => {
data.push({ 'name': category.menuItemCateName, 'items': [b] })
})
})
But I am getting this result:
[
{ name: 'Popular', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"} ] },
{ name: 'Featured', items: [ { "Id": 1, "price": 10, "itemName": "Spicy burger"} ] }
]
In the Featured category there should be two items data, but i am getting one item data because I am doing category.itemId[0] inside the filter function, which i have to loop inside the value of property itemId which i have no idea how to accomplish it.
I will appreciate any help.
Using map() to create new object and using filter() to add only matching itemIds
const categories = [
{"itemId": [1], "menuItemCateName": "Popular"},
{"itemId": [1, 2], "menuItemCateName": "Featured"}
]
const items = [
{
"Id": 1,
"price": 10,
"itemName": "Spicy burger"
},
{
"Id": 2,
"price": 10,
"itemName": "Pizza"
}
]
let result = categories.map(cat => {
let newObject = { // creating new object with desired properties
name: cat.menuItemCateName,
items: items.filter(item => cat.itemId.some(s => s === item.Id)) // filtering matching its object
}
return newObject;
})
console.log(result)
console.log('********* second object items have to values: ',result[1].items.length )
console.log(result[1].items)

Return new form of arrays using lodash methods

My first array:
[
{
"id": 1,
"name": "Gilman",
"entryCheckPointId": 1,
"entryCheckPoint": {
"id": 1,
"name": "Checkpoint A"
}
},
{
"id": 2,
"name": "Arshad",
"entryCheckPointId": 3,
"entryCheckPoint": {
"id": 3,
"name": "Checkpoint C"
}
},
{
"id": 3,
"name": "Illyas",
"entryCheckPointId": 3,
"entryCheckPoint": {
"id": 3,
"name": "Checkpoint C"
}
}
]
Second Array:
[
{
"entryCheckPointId": 1,
"count": 1
},
{
"entryCheckPointId": 3,
"count": 2
}
]
I want to match these both using entryCheckPointId and i want to return a new form of array like this
Expected Output:
[
{
label: 'Checkpoint A',
value: 1, // count
color: 'green'
},
{
label: 'Checkpoint C',
value: 2, // count
color: 'pink'
},
]
I tried with map function, but it is not working correctly as i expected.
I tried this code:
var mergedList = _.map(a1, function(item){
return _.extend(item, _.findWhere(a2, { id: item.id }));
}); // a1 is firstArray and a2 is secondArray
Is this possible to do using any of the lodash library?
And one doubt, is lodash and underscore both are same?
In plain Javascript (which I guess is not what you are looking for but it may be helpful):
var x = [{"id": 1,"name": "Gilman","entryCheckPointId": 1,"entryCheckPoint": {"id": 1,"name": "Checkpoint A"}},{"id": 2,"name": "Arshad","entryCheckPointId": 3,"entryCheckPoint": {"id": 3,"name": "Checkpoint C"}},{"id": 3,"name": "Illyas","entryCheckPointId": 3,"entryCheckPoint": {"id": 3,"name": "Checkpoint C"}}]
var y = [ { "entryCheckPointId": 1, "count": 1},{ "entryCheckPointId": 3, "count": 2 }]
var colors = ["pink", "red", "green"]
var result = y.map((a,i)=>({
label: x.filter(b=>b.entryCheckPointId == a.entryCheckPointId)[a.count-1].entryCheckPoint.name,
value: a.count,
color: colors[i]
}))
console.log(result)
Please find two examples of how this looks with lodash and without. Both examples utilize some of the new ES6 features (like shorthand object literals , object destructuring etc).
var data1 = [{ "id": 1, "name": "Gilman", "entryCheckPointId": 1, "entryCheckPoint": { "id": 1, "name": "Checkpoint A" }}, { "id": 2, "name": "Arshad", "entryCheckPointId": 3, "entryCheckPoint": { "id": 3, "name": "Checkpoint C" }}, { "id": 3, "name": "Illyas", "entryCheckPointId": 3, "entryCheckPoint": { "id": 3, "name": "Checkpoint C" }}]
var data2 = [ { "entryCheckPointId": 1, "count": 1 }, { "entryCheckPointId": 3, "count": 2 } ]
var colors = ['red', 'green', 'blue']
var lodashResult = _.map(data2, ({entryCheckPointId, count}, i) => ({
label: _.find(data1, {entryCheckPointId}).entryCheckPoint.name,
count,
color: colors[i]
}))
var noLodashResult = data2.map(({entryCheckPointId, count}, i) => ({
label: data1.find(x => x.entryCheckPointId == entryCheckPointId).entryCheckPoint.name,
count,
color: colors[i]
}))
console.log('lodash', lodashResult)
console.log('noLodash', noLodashResult)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>
As you can see the _.lodash is somewhat simpler and shorter.

create new json from existing json using AngularJS or Javascript

Category JSON
I am getting this JSON by accessing API and soring it in $scope.categoryList
[
{
"id": 1,
"name": "Men"
},
{
"id": 2,
"name": "Women"
},
{
"id": 3,
"name": "Kids"
}
]
SubCategory JSON
I am getting this JSON by accessing API and soring it in $scope.subCategoryList
[
{
"id": 1,
"category_id": 1,
"name": "Footwear"
},
{
"id": 2,
"category_id": 2,
"name": "Footwear"
},
{
"id": 3,
"category_id": 1,
"name": "Cloths"
}
]
I need to design this in below format
[
{
"categoryId" : 1,
"categoryName" : "Men",
"subCategory" : [
{
"subCategoryId": 1,
"subCategoryName": "Footwear"
},
{
"subCategoryId": 3,
"subCategoryName": "Cloths"
},
]
},
{
"categoryId" : 2,
"categoryName" : "Women",
"subCategory" : [
{
"subCategoryId": 2,
"subCategoryName": "Footwear"
}
]
},
{
"categoryId" : 3,
"categoryName" : "Kids",
"subCategory" : []
}
]
I have the code but it is not showing perfect data
$scope.catSubCat = []
angular.forEach($scope.subcategoryList, function(subValue, subKey) {
$scope.subCat = {
'subCategoryId' : '',
'subCategoryName' : ''
}
angular.forEach($scope.categoryList, function(catValue, catKey) {
if(subValue.category_id == catValue.id) {
$scope.subCat.subCategoryId = subValue.id;
$scope.subCat.subCategoryName = subValue.name;
$scope.subCategory = {
'categoryId' : '',
'categoryName' : '',
'subCatName' : []
}
$scope.catVal.categoryId = subValue.category_id;
$scope.catVal.categoryName = catValue.name;
$scope.catVal.subCatName.push($scope.subCat);
}
$scope.catSubCat.push($scope.catVal);
});
});
This should do the trick. Not as clean as 31piy's (wow!) but more efficient. (O(N + M) as opposed to O(N * M))
const categoryList = [
{
"id": 1,
"name": "Men"
},
{
"id": 2,
"name": "Women"
},
{
"id": 3,
"name": "Kids"
}
];
const subCategoryList = [
{
"id": 1,
"category_id": 1,
"name": "Footwear"
},
{
"id": 2,
"category_id": 2,
"name": "Footwear"
},
{
"id": 3,
"category_id": 1,
"name": "Cloths"
}
];
const mergeCategoryLists = (categoryList, subCategoryList) => {
// Turn categoryList into an object with categoryId as key
const categoryById = {};
categoryList.forEach((category) => {
categoryById[category.id] = {
categoryName: category.name,
categoryId: category.id,
subCategory: []
};
});
// Add subcategories
subCategoryList.forEach((subCategory) => {
const formattedSubCategory = {
subCategoryId: subCategory.id,
subCategoryName: subCategory.name
};
categoryById[subCategory.category_id].subCategory.push(formattedSubCategory);
});
// Convert categoryById into desired format
return Object.values(categoryById);
};
console.log(mergeCategoryLists(categoryList, subCategoryList));
Check out this logic .
$scope.newArray = angular.copy($scope.categoryList);
$scope.catSubCat = []
angular.forEach($scope.subcategoryList, function(subValue, subKey) {
$scope.subCat = {
'subCategoryId' : '',
'subCategoryName' : ''
}
angular.forEach($scope.newArray, function(catValue, catKey) {
$scope.subCat.subCategoryId = subValue.id;
$scope.subCat.subCategoryName = subValue.name;
if(subValue.category_id == catValue.id) {
if(catValue.subCatName.hasOwnProperty('bye')){
$scope.newArray[catKey].subCatName = [];
$scope.newArray[catKey].subCatName.push($scope.subCat);
}else{
$scope.newArray[catKey].subCatName.push($scope.subCat);
}
}
});
});
Resultant will we in $scope.newArray
You can use Array#map in combination with Array#filter to achieve the desired results:
var categories = [{
"id": 1,
"name": "Men"
},
{
"id": 2,
"name": "Women"
},
{
"id": 3,
"name": "Kids"
}
];
var subcategories = [{
"id": 1,
"category_id": 1,
"name": "Footwear"
},
{
"id": 2,
"category_id": 2,
"name": "Footwear"
},
{
"id": 3,
"category_id": 1,
"name": "Cloths"
}
];
var result = categories.map(cat => {
return {
categoryId: cat.id,
categoryName: cat.name,
subCategory: subcategories
.filter(subc => subc.category_id === cat.id)
.map(subc => {
return {
subCategoryId: subc.id,
subCategoryName: subc.name
};
})
};
});
console.log(result);
var categoryList = [{
"id": 1,
"name": "Men"
}, {
"id": 2,
"name": "Women"
}, {
"id": 3,
"name": "Kids"
}];
var subCategoryList = [{
"id": 1,
"category_id": 1,
"name": "Footwear"
}, {
"id": 2,
"category_id": 2,
"name": "Footwear"
}, {
"id": 3,
"category_id": 1,
"name": "Cloths"
}];
var finalJson = [];
for (var i = 0; i < categoryList.length; i++) {
var obj = {
categoryId: categoryList[i].id,
categoryName: categoryList[i].name,
subCategory: []
};
var subCat = subCategoryList.filter(function(word) {
return word.category_id === categoryList[i].id;
});
for (var j = 0; j < subCat.length; j++) {
var obj2 = {
subCategoryId: subCat[j].id,
subCategoryName: subCat[j].name
};
obj.subCategory.push(obj2);
}
finalJson.push(obj);
}
console.log(finalJson);
Pure Javascript solution to your question, you can replace with
Angular Syntax then..
Use following code:
$scope.catSubCat = []
angular.forEach($scope.categoryList, function(catValue, catKey) {
var catObj = {
'categoryId' : '',
'categoryName' : '',
'subCatName' : []
}
catObj.categoryId = catValue.id;
catObj.categoryId = catValue.name;
angular.forEach($scope.subcategoryList, function(subValue, subKey) {
if(subValue.category_id == catValue.id) {
var subCatObj = {
'subCategoryId' : '',
'subCategoryName' : ''
}
subCatObj.subCategoryId = subValue.category_id;
subCatObj.subCategoryName = catValue.name;
catObj.subCatName.push(subCatObj);
}
});
$scope.catSubCat.push(catObj);
});

Create a string from a specific key/value in an array

I have an array collection that I want to pull values out of to create a string. I am using lodash to help out with the loop.
var arr = [
{
"id": 0,
"name": "Lorem"
},
{
"id": 1,
"name": "ipsum"
},
{
"id": 3,
"name": "dolor"
},
{
"id": 4,
"name": "sit"
}
];
_.forEach(arr, function(value) {
var jawn = value.name;
jawn.join('\n');
console.log(jawn);
});
What I want to do is
jawn.join('\n');
So the result for console.log(jawn); would be...
"Lorem
ipsum
dolor
sit
amet"
I know this but I'm drawing a blank for some reason.
I'm not familiar with lodash, but you can easily achieve the desired result using Array#map. I bet it would be pretty similar with lodash.
var arr = [ { "id": 0, "name": "Lorem" }, { "id": 1, "name": "ipsum" }, { "id": 3, "name": "dolor" }, { "id": 4, "name": "sit" } ],
res = arr.map(v => v.name).join('\n');
console.log(res);
Without lodash:
arr.map(function(value) {
return value.name;
}).join('\n');
You could map only the name and join the array with \n.
var arr = [{ id: 0, name: "Lorem" }, { id: 1, name: "ipsum" }, { id: 3, name: "dolor" }, { id: 4, name: "sit" }],
result = arr.map(a => a.name).join('\n');
console.log(result);

Construct flat array from tree of objects

Suppose I have a tree of objects like the following, perhaps created using the excellent algorithm found here: https://stackoverflow.com/a/22367819/3123195
{
"children": [{
"id": 1,
"title": "home",
"parent": null,
"children": []
}, {
"id": 2,
"title": "about",
"parent": null,
"children": [{
"id": 3,
"title": "team",
"parent": 2,
"children": []
}, {
"id": 4,
"title": "company",
"parent": 2,
"children": []
}]
}]
}
(Specifically in this example, the array returned by that function is nested as the children array property inside an otherwise empty object.)
How would I convert it back to a flat array?
Hope your are familiar with es6:
let flatten = (children, extractChildren) => Array.prototype.concat.apply(
children,
children.map(x => flatten(extractChildren(x) || [], extractChildren))
);
let extractChildren = x => x.children;
let flat = flatten(extractChildren(treeStructure), extractChildren)
.map(x => delete x.children && x);
UPD:
Sorry, haven't noticed that you need to set parent and level. Please find the new function below:
let flatten = (children, getChildren, level, parent) => Array.prototype.concat.apply(
children.map(x => ({ ...x, level: level || 1, parent: parent || null })),
children.map(x => flatten(getChildren(x) || [], getChildren, (level || 1) + 1, x.id))
);
https://jsbin.com/socono/edit?js,console
This function will do the job, plus it adds a level indicator to each object. Immediate children of treeObj will be level 1, their children will be level 2, etc. The parent properties are updated as well.
function flatten(treeObj, idAttr, parentAttr, childrenAttr, levelAttr) {
if (!idAttr) idAttr = 'id';
if (!parentAttr) parentAttr = 'parent';
if (!childrenAttr) childrenAttr = 'children';
if (!levelAttr) levelAttr = 'level';
function flattenChild(childObj, parentId, level) {
var array = [];
var childCopy = angular.extend({}, childObj);
childCopy[levelAttr] = level;
childCopy[parentAttr] = parentId;
delete childCopy[childrenAttr];
array.push(childCopy);
array = array.concat(processChildren(childObj, level));
return array;
};
function processChildren(obj, level) {
if (!level) level = 0;
var array = [];
obj[childrenAttr].forEach(function(childObj) {
array = array.concat(flattenChild(childObj, obj[idAttr], level+1));
});
return array;
};
var result = processChildren(treeObj);
return result;
};
This solution takes advantage of Angular's angular.extend() function to perform a copy of the child object. Wiring this up with any other library's equivalent method or a native function should be a trivial change.
The output given for the above example would be:
[{
"id": 1,
"title": "home",
"parent": null,
"level": 1
}, {
"id": 2,
"title": "about",
"parent": null,
"level": 1
}, {
"id": 3,
"title": "team",
"parent": 2,
"level": 2
}, {
"id": 4,
"title": "company",
"parent": 2,
"level": 2
}]
It is also worth noting that this function does not guarantee the array will be ordered by id; it will be based on the order in which the individual objects were encountered during the operation.
Fiddle!
Here it goes my contribution:
function flatNestedList(nestedList, childrenName, parentPropertyName, idName, newFlatList, parentId) {
if (newFlatList.length === 0)
newFlatList = [];
$.each(nestedList, function (i, item) {
item[parentPropertyName] = parentId;
newFlatList.push(item);
if (item[childrenName] && item[childrenName].length > 0) {
//each level
flatNestedList(item[childrenName], childrenName, parentPropertyName, idName, newFlatList, item[idName]);
}
});
for (var i in newFlatList)
delete (newFlatList[i][childrenName]);
}
Try following this only assumes each item is having children property
class TreeStructureHelper {
public toArray(nodes: any[], arr: any[]) {
if (!nodes) {
return [];
}
if (!arr) {
arr = [];
}
for (var i = 0; i < nodes.length; i++) {
arr.push(nodes[i]);
this.toArray(nodes[i].children, arr);
}
return arr;
}
}
Usage
let treeNode =
{
children: [{
id: 1,
title: "home",
parent: null,
children: []
}, {
id: 2,
title: "about",
parent: null,
children: [{
id: 3,
title: "team",
parent: 2,
children: []
}, {
id: 4,
title: "company",
parent: 2,
children: []
}]
}]
};
let flattenArray = _treeStructureHelper.toArray([treeNode], []);
This is data:
const data = {
id: '1',
children: [
{
id: '2',
children: [
{
id: '4',
children: [
{
id: '5'
},
{
id: '6'
}
]
},
{
id: '7'
}
]
},
{
id: '3',
children: [
{
id: '8'
},
{
id: '9'
}
]
}
]
}
In React.JS just declare an array field in state and push items to that array.
const getAllItemsPerChildren = item => {
array.push(item);
if (item.children) {
return item.children.map(i => getAllItemsPerChildren(i));
}
}
After function call your array in state will hold all items as below:
One more 😄😁
function flatten(root, parent=null, depth=0, key='id', flat=[], pick=() => {}) {
flat.push({
parent,
[key]: root[key],
depth: depth++,
...pick(root, parent, depth, key, flat)
});
if(Array.isArray(root.children)) {
root.children.forEach(child => flatten(child, root[key], depth, key, flat, pick));
}
}
let sample = {
"id": 0,
"children": [{
"id": 1,
"title": "home",
"parent": null,
"children": []
}, {
"id": 2,
"title": "about",
"parent": null,
"children": [{
"id": 3,
"title": "team",
"parent": 2,
"children": []
}, {
"id": 4,
"title": "company",
"parent": 2,
"children": []
}]
}]
};
let flat = [];
flatten(sample, null, 0, 'id', flat, root => ({ title: root.title }));
let expected = [
{
"id": 0,
"parent": null,
"depth": 0
},
{
"id": 1,
"parent": 0,
"depth": 1,
"title": "home"
},
{
"id": 2,
"parent": 0,
"depth": 1,
"title": "about"
},
{
"id": 3,
"parent": 2,
"depth": 2,
"title": "team"
},
{
"id": 4,
"parent": 2,
"depth": 2,
"title": "company"
}
];

Categories