Count iterations of an object inside an array - javascript

I have a variable returning an array from the DB. Inside that array I need to count the instances of a specific object iteration.
I tried using jQuery.each and logging the output to check but was just getting individual results in the console. I was not getting the desired count. In the sample below I would expect to see:
size 2 - count 1
size 4 - count 2
Sample Output
0: {Email: "example#mai.com", Name: "Bob Smith", ​Number: "555-555-1234", Size: "2"}
1: { Email: "example2#mai.com", Name: "Jenny Girl", ​Number: "222-333-1234", Size: "4"}
2: { Email: "example3#mai.com", Name: "Johnny on the spot", ​Number: "111-777-1234", Size: "4"}
Using jquery what is the best way to achieve this? Is using something like .size?
My code I have attempted:
​​function xyz() {
jQuery.ajax({
data: {action: 'my_action'},
type: 'post',
url: my_ajax.ajax_url,
dataType: 'JSON',
success: function(data) {
jQuery.each(data, function(key,val){
var n = Object.keys(val.Size).size;
console.log(n);
});
​​
​​This gives me an 'undefined' console log readout.

You could store the value of Size as the property of an Object and count:
// Just for demo - that's your data response:
const data = [{Size: "2"}, {Size: "4"}, {Size: "4"}, {Size: "99"}];
const sizes = data.reduce((ob, item) => {
if (!ob.hasOwnProperty(item.Size)) ob[item.Size] = 0;
++ob[item.Size];
return ob;
}, {});
console.log(sizes);

var data = [
{Email: "example#mai.com", Name: "Bob Smith", Number: "555-555-1234", Size: "2"},
{Email: "example2#mai.com", Name: "Jenny Girl", Number: "222-333-1234", Size: "4"},
{Email: "example3#mai.com", Name: "Johnny on the spot", Number: "111-777-1234", Size: "4"}
];
const sizes = data
// collect each "Size" value from the data
.map(e => e.Size)
// use an object to count each instance
.reduce((a, n) => ({...a, [n]: (a[n] || 0) + 1 }), {})
// log each object entry to the console
Object.entries(sizes)
.forEach(([size, count]) => console.log(`size: ${size} count: ${count}`));

Related

retrieving data from nested object

I have this object example only:
{
id: 301
payload: {
1: {
house_no: 1234,
city: London
},
2: {
house_no: 0000
city: Paris
}
}
}
I need to extract only the house_no and city for both 1 and 2.
I have tried for loping through like so: *Address passed to the loop is the object above
let item = [];
for(let x in address){
item.push(item[x]);
}
console.log('Address', item.city);
This gives me array with undefined elements:
[
0: undefined
1: undefined
]
Could you guys please help me just retrieving the data i need for each 1,2 : house_no and city
Object.values(address.payload).map(({ house_no, city }) => ({ house_no, city }));
This goes over each value in address.payload and returns an array of objects with house_no and city.
const address = {
id: 301,
payload: {
1: {
house_no: 1234,
city: 'London'
},
2: {
house_no: 0000,
city: 'Paris'
}
}
};
const result = Object.values(address.payload).map(({ house_no, city }) => ({ house_no, city }));
console.log(result);
You can just directly access it as below,
const {1: obj1, 2: obj2} = address.payload;
In here javascript will destructure the payload and assign object 1 into 1 and object 2 into 2. There were some missing commas and quotations in the object which you provided. Therefore I added that also below. Now, obj1 and obj2 will have the 1 and 2 objects where you can extract the data you need out of them easily.
let address = {
id: 301,
payload: {
1: {
house_no: 1234,
city: "London"
},
2: {
house_no: 0000,
city: "Paris"
}
}
};
const {1: obj1, 2: obj2} = address.payload;
console.log(obj1);
console.log(obj1.city);
console.log(obj1.house_no);
You can access object properties directly without looping:
<script>
var obj = {
id: 301,
payload: {
1: {
house_no: 1234,
city: "London"
},
2: {
house_no: 0000,
city: "Paris"
}
}
}
var address1 = obj.payload[1].house_no + " " + obj.payload[1].city;
var address2 = obj.payload[2].house_no + " " + obj.payload[2].city;
</script>

rxjs: recursive Observable emission inside map

I am pulling my hair out a little with attempting to group data recursively in rxjs. There seems to be alot of good examples around with different use cases but I cant seem to refactor the code around to fit my requirements.
The central problem that I can infer is that in my map operator I have a conditional return which is either a non-observable or an observable. Is there a way I can refactor to account for this discrepency?
Ideally this function would group an original "flat" array by an arbitrary amount of columns that are passed in as the cols argument.
Each time the criteria is satisfied it would
append to an array in the format
{ key: col_name, elements : [...col_elements] } where elements would be another array of elements in the same format, or a straight list of elements.
The below function works when only grouping the columns once ( and thus never requiring the observable within map to emit ).
//group data n times based on passed string[] of column attributes
group_data(elements: Observable<any>, cols: string[], index=0) : Observable<any> {
let col = cols[index]
return elements.pipe(
//groupby column value
RxOp.groupBy((el:any) => this.get_groupingValue(el, col)),
//place key inside array
RxOp.mergeMap((group) => group.pipe(
RxOp.reduce((acc, cur) => [...acc, cur], ["" + group.key]))
),
// map to key:group
RxOp.map((arr:any) =>
cols.length <= index + 1?
// no more grouping req
{ 'key': arr[0], 'elements': arr.slice(1) } :
//group recursively, returns Observable instead of array:(
{ 'key': arr[0], 'elements':
this.group_data(from(arr.slice(1)), cols, index + 1)
.pipe(
RxOp.tap(t=> console.log(t)), // not logged
RxOp.reduce((acc, val) => [...acc, val], [])
)
}),
RxOp.toArray()
)
}
//simplified data example:
data = [{id: 'idA', type: 'project', parents: null },
{id: 'idB', type: 'project', parents: null },
{id: 'idC', type: 'episode', parents: ['idA'] },
{id: 'idD', type: 'episode', parents: ['idB'] },
{id: 'idE', type: 'scene', parents: ['idA', 'idC'] },
{id: 'idF', type: 'scene', parents: ['idB', 'idD'] }]
// 1 column passed works correctly as below
group_data(elements: from(data), ['project'])
/* outputted data:
[{key: 'idA',
elements: [ {id: 'idC', type: 'episode', parents: ['idA'] },
{id: 'idE', type: 'scene', parents: ['idA', 'idC'] }]},
{key: 'idB',
elements: [ {id: 'idD', type: 'episode', parents: ['idA'] },
{id: 'idF', type: 'scene', parents: ['idA', 'idC'] }]},
{key: null,
elements: [ {id: 'idA', type: 'project', parents: [] }
{id: 'idB', type: 'project', parents: [] }]}
]
*/
// 2 columns not working correctly
group_data(elements: from(data), ['project', 'episode'])
/*[{key: 'idA',
elements: Observable},
{key: 'idB',
elements: Observable},
{key: null,
elements: Observable}
]*/
The approach i needed to take was to restructure so that I could use mergeMap instead of map - which meant i needed two observables at the condition instead of one. From there i just needed to refactor so that the mapping to key, elements was done after the mergeMap.
This is still my first foray into rxjs, so my explanation isn't great and my summary of the problem also wasnt great. Importantly, at least its behaving as expected now.
//group data n times based on passed string[] of column attributes
group_data(elements: Observable<any>, cols: string[], index=0) : Observable<any> {
let col = cols[index]
let grouping = elements.pipe(
//groupby column value
RxOp.groupBy((el:any) => this.get_groupingValue(el, col)),
//place key inside array
RxOp.mergeMap((group) => group.pipe(
RxOp.reduce((acc, cur) => [...acc, cur], ["" + group.key]))
)
)
return grouping.pipe(
RxOp.mergeMap((arr) =>
(
cols.length <= (index +1) ?
//no more grouping required
of(arr.slice(1)) :
//group again
this.group_data(from(arr.slice(1)), cols, index + 1))
// reduce result and put the key back in
.pipe(
RxOp.reduce((acc, cur) => [...acc, cur], ["" + arr[0]])
)
),
// map to key:group
RxOp.map(arr => ({
key: arr[0],
elements: arr.slice(1)
})
),
RxOp.toArray()
)

Convert Object Array To Array With Calculation

I am working with the following object array and attempting to convert it into an array:
const data = [
{
count: 3,
userName: "Paul Crewe",
value: "Activity Type",
},
{
count: 1,
userName: "Nate Scarborough",
value: "Activity Type",
},
{
count: 1,
userName: "Nate Scarborough",
value: "Another Activity Type",
},
{
count: 1,
userName: "Paul Crewe",
value: "Another Activity Type",
},
];
Expected Outcome:
const outcome = [
['userName', 'Paul Crewe', 'Nate Scarborough'],
['Activity Type', 3, 1],
['Another Activity Type', 1, 1]
];
The outcome array takes the data and uses the userName key to create to first array element followed by the format of value, count for each additional array element. For example,
['userName', 'Paul Crewe', 'Nate Scarborough'],
[{value}, {count for Paul Crewe}, {count for Nate Scarborough} ],
I feel that using a reduce is appropriate and have started with:
data.reduce((a, c) => {
a[c.userName] = { value: c.value, count: c.count };
a[c.userName].count += c.count;
return a;
}, {});
But this results in an undesired outcome like:
{
Nate Scarborough: {value: "Another Activity Type", count: 2},
Paul Crewe: {value: "Another Activity Type", count: 2},
}
You could start with the key userName and build new value rows as requires. It works with anrbitrary count of values.
This solution could return an array of sparse arrays. If not wanted, then you need to map the inner array with a default zero.
const
data = [{ count: 3, userName: "Paul Crewe", value: "Activity Type" }, { count: 1, userName: "Nate Scarborough", value: "Activity Type" }, { count: 1, userName: "Nate Scarborough", value: "Another Activity Type" }, { count: 1, userName: "Paul Crewe", value: "Another Activity Type" }],
result = data.reduce((r, o) => {
var vIndex = r.findIndex(([v]) => v === o.value),
index = r[0].indexOf(o[r[0][0]]);
if (vIndex < 0) {
vIndex += r.push([o.value]);
}
if (index < 0) {
index += r[0].push(o[r[0][0]]);
}
r[vIndex][index] = (r[vIndex][index] || 0) + o.count;
return r;
}, [['userName']]);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I’m a bit old-school when it comes to working with arrays - I am more comfortable with doing my own loops, than the various “fancy” array methods.
Here’s how I would handle this,
const data = [
{
count: 3,
userName: "Paul Crewe",
value: "Activity Type",
},
{
count: 1,
userName: "Nate Scarborough",
value: "Activity Type",
},
{
count: 1,
userName: "Nate Scarborough",
value: "Another Activity Type",
},
{
count: 1,
userName: "Paul Crewe",
value: "Another Activity Type",
},
];
const temp = {
userName : []
};
data.forEach(function(e) {
// push username only if not in array already
if(temp.userName.indexOf(e.userName) === -1) {
temp.userName.push(e.userName);
}
// create empty array for activity name, if not exists yet
if(!temp[e.value]) {
temp[e.value] = [];
}
temp[e.value].push(e.count)
});
var outcome = [];
// special treatment for userName, to make sure that comes first
temp.userName.unshift('userName');
outcome.push(temp.userName);
for(k in temp) {
if(k != 'userName') {
temp[k].unshift(k); // insert activity name at front of array
outcome.push( temp[k]); // insert array into final result array
}
}
console.log(outcome)
Using the temp helper object makes it easier to access the correct array using the activity name - if I went with the desired structure directly, that would mean looping over the arrays and comparing the first entry all the time to find the right one, whereas with an object a simple property lookup will do.

Lodash orderBy returning empty array

I am trying to order an array with Objects in ascending order using lodash.orderBy npm module.
this is the example object I am using
var obj = [{ user: 'me', score: 100}, { user: 'you', score: 55}, { user: 'someone', score: 555 }]
Once I have that object, I pass it through as the first parameter and specify which key to orderBy.
let sort = orderBy(obj, 'score')
But this returns an empty array, is there something I am doing wrong ? based on the documentation this is correct: https://lodash.com/docs/4.17.4#orderBy
This may be an issue with the asynchronous call and saying the index of the Array is 0.
EDIT:
It seems as if you are not actually calling orderBy() on the lodash object. Try the following code:
JS
const _ = require('lodash');
var obj = [[{
user: 'me',
score: 100
},{
user: 'you',
score: 55
},{
user: 'someone',
score: 555
}]]
var data = obj[0];
let sort = _.orderBy(data, 'score');
console.log(sort);
Created a new variable data and assign it the value of obj[0] effectively assigning it the array. Lodash can then order the data as expected.
Console Output (score in ascending order by default)
[ { user: 'you', score: 55 },
{ user: 'me', score: 100 },
{ user: 'someone', score: 555 } ]
Possible solution for your code
let obj = this.state.items;
let data = obj[0];
let sort = lodash.orderBy(data, 'score');
console.log(sort);

Angular POS app, trying to generate an automatic purchase order from a POJO

I'm x-referencing a few things inside of a products object, namely, stock levels, low stock levels, suppliers, min-order qty, and cost. The method should create an order object for each supplier with each product whose stock is lower than low-stock levels.
Using this object format:
0: {
$$hashKey: "081",
$id: "-JPuOUmkvwpXB-99QuU6",
brand: "",
categories: Array[2],
events: Array[1],
imgUrl: "http://crayons-roleplay.weebly.com/uploads/2/4/9/6/24964708/735664_orig.jpg",
lowStock: "10",
messages: Array[1],
minOrder: "25",
name: "Crazy Crayons - Eco stars (individual)",
notes: Array[1],
price: "0.5",
quantity: "",
size: "",
sku: "912143",
stock: "21",
suppliers: [
0: {,
caseSize: ""
name: "Crazy Crayons"
price: 0.29
sku: ""
units: ""
tax: "15"
units: ""
upc: "91214"
updated: 1404907608273
}
]
1: {
$$hashKey: "082"
$id: "-JPuOUmnj9r6wGx27qVE"
brand: ""
categories: Array[1]
events: Array[1]
lowStock: 0
messages: Array[1]
minOrder: 4
name: "Lifefactory 12 oz Straw Cap - Coral"
notes: Array[1]
price: "26"
quantity: ""
size: ""
sku: "45635971011"
stock: "0"
suppliers: Array[1]
0: Object
caseSize: ""
name: "SOKO"
price: 13
sku: ""
units: ""
tax: ""
units: ""
upc: "45635971011"
updated: "2014-07-07T17:02:49.089Z"
}
action:function(){
Making a list of products with low stock:
angular.forEach(data.products.array, function(value){
if(value.stock < value.lowStock){
if(angular.isObject(value)){
$scope.generatedOrder.push({id:value.$id,upc:value.upc,'min-order':value.minOrder,supplier:value.suppliers});
}
}
});
$scope.supplierList = [];
$scope.supplierResults = [];
Pulling a list of suppliers from low-stock products:
angular.forEach($scope.generatedOrder, function(order){
angular.forEach(order.supplier, function(supplier){
if ($scope.supplierList.indexOf(supplier.name) == -1) {
$scope.supplierList.push(supplier.name);
}
});
});
angular.forEach($scope.supplierList, function(supplier){
$scope.supplierResults[supplier]=[];
});
Troublesome facet, I've tried many different ways to create an order key'd by the supplier. The closest I've come to victory is an array of supplier objects, with the right key but no value.
for(var i=0;i<Object.keys($scope.generatedOrder).length ;i++){
for(var j=0;j<$scope.supplierList.length;j++){
if (!angular.isArray(supplierResults[$scope.supplierList[j]])){
supplierResults[$scope.supplierList[j]] = [];
}
if ($scope.generatedOrder[i].supplier !== undefined){
if ( $scope.generatedOrder[i].supplier['0'].name === $scope.supplierList[j]) {
supplierResults[$scope.supplierList[j]].unshift($scope.generatedOrder[i]);
$scope.supplierResults = supplierResults;
}
}
}
}
Now, this almost works. The proper objects are built up and show in the array but the array reads length: 0 so I tried to have the length of $scope.supplierResults = supplierResults in here to make the browser think the array is full and normal.
$scope.supplierResults.length = supplierResults.length;
console.log($scope.supplierResults);
return $scope.supplierResults;
The end result should be like $scope.supplierResults = [{supplier:'xyz', items:[{id:'4g245t2g424g45t',name:apple, sku:453524, cost:3, qty:10}]}, {supplier:'abc',items:[{id:'3982462398uih23j',orange, sku:32545, cost:2, qty:12}]}]
EDIT: Here is updated, correct code. I accidentally left the target out of the "omit" and forgot to call value() to end my chain in the inner function right after the omit.
var result = _(data)
.filter(function(product){
return product.stock < product.lowStock && _.isObject(product);
})
.map(function(product){
return _(product.suppliers).map(function(supplier){
return {supplier:supplier, product : _.omit(product, 'suppliers')};
}).value();
})
.flatten()
.groupBy(function(productAndSupplier){
return productAndSupplier.supplier.name;
})
.map(function(productAndSupplierGroup){
return _(productAndSupplierGroup).reduce(function(memo, current){
memo.supplier = current.supplier;
memo.items.push(current.product);
return memo;
},{items:[]})
})
.value();
And a plunkr to show it in action: http://plnkr.co/edit/XAiyWauCdKmHTS3unqO5 (note that you will have to look in the browser console to see the output)

Categories

Resources