I am trying to add the unique values for the fiddle below in the output I want
{ category: 'fos', value: 70 },
{ category: 'nyedva', value: 30 }
I am able to get the unique values in the array not sure where to add the values
http://jsfiddle.net/mj3q0sk3/
var catalog={
products : [
{ category: 'fos', value: 10 },
{ category: 'fos', value: 20 },
{ category: 'nyedva', value: 30 },
{ category: 'fos', value: 40 },
]
};
var categories = [];
var sum=[];
$.each(catalog.products, function(index, value) {
if ($.inArray(value.category, categories)==-1) {
categories.push(value.category);
}
else {
console.log("CAt Val:" +value.category);
var total=value.value;
sum.push(total);
}
});
console.log(categories);
console.log(sum);
You can use forEach() loop to return desired result.
var catalog = {"products":[{"category":"fos","value":10},{"category":"fos","value":20},{"category":"nyedva","value":30},{"category":"fos","value":40}]}
var result = [];
catalog.products.forEach(function(e) {
var c = e.category;
!this[c] ? (this[c] = e, result.push(this[c])) : this[c].value += e.value
}, {})
console.log(result)
You can do this without the need for jQuery:
var res = catalog.products.reduce(function(res, product) {
if (!res.hasOwnProperty(product.category)) {
res[product.category] = 0;
}
res[product.category] += product.value;
return res;
}, {});
console.log(res);
This yields:
{ fos: 70, nyedva: 30 }
If you want it as an array of categories:
console.log(Object.keys(res).map(function(key) { return { category: key, value: res[key] }; }));
This will give you:
[ { category: 'fos', value: 70 },
{ category: 'nyedva', value: 30 } ]
Related
Im trying to build a new array of objects based on specs array of objects.
There are some similar question already but didn't help me.
const specs =
[
{ label: 'Brand', value: 'Nike' },
{ label: 'Age range', value: '8 to 13' },
{ label: 'Age range', value: '14+' }
]
I want to skip the "Brand", which works with my code, and I want that if there are multiple objects with the same label, they need
to be merged in a unique object which contains that label and ALL of the values together, so the output will have brand removed and the other values merged:
[
{ label: 'Age range', value: '8 to 13 - 14+' }
]
Here's my code:
var newSpecs = []
for (let x = 0; x < specs.length; x++) {
if (specs[x].label === 'Brand') continue
for (let j = x + 1; j < specs.length; j++) {
if (specs[x].label === specs[j].label) {
specs[x].value += ' - ' + specs[j].value
}
}
newSpecs.push({ label: specs[x].label, value: specs[x].value })
}
return newSpecs
But this simply does not work, it created multiple merged age range.
I tried another time by using delete and other things but got me "cannot read label of undefine", so I'll keep this way,
but I cannot figure out how to do it
You receive multiple "Age range" because you have nested loops. Try this approach using .reduce() for grouping:
const specs = [
{ label: 'Brand', value: 'Nike' },
{ label: 'Age range', value: '8 to 13' },
{ label: 'Age range', value: '14+' }
];
var newSpecs = specs.reduce((res, curr) => {
if (curr.label === 'Brand') return res;
var group = res.find((el) => el.label === curr.label);
if (group) {
group.value += ' - ' + curr.value;
} else {
res.push(curr);
}
return res;
}, []);
console.log(newSpecs);
You can use map to separate out labels
const specs = [
{ label: "Brand", value: "Nike" },
{ label: "Age range", value: "8 to 13" },
{ label: "Age range", value: "14+" },
];
const labelMap = {};
specs.forEach((sp) => {
if (sp.label !== "Brand") {
labelMap[sp.label] = labelMap[sp.label] || { label: sp.label, value: [] };
labelMap[sp.label].value.push(sp.value);
}
});
const op = Object.values(labelMap).map((el) => ({
...el,
value: el.value.join(" - "),
}));
console.log(op);
I currently have an array that has the following structure:
data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
But I would like to restructure the array to get something like this:
data = [
{
name: "thing1",
info: [{
time: 100,
count: 3
}, {
time: 1000,
count: 7
}, {
}]
},
{
name: "thing2",
info: [{
time: 100,
count: 2
}, {
time: 1000,
count: 0
}, {
}]
}
];
So basically the key would have to be switched from time to name, but the question is how. From other posts I have gathered that using the map function might work, but since other posts had examples to and from different structures I am still not sure how to use this.
There are a number of ways to achieve this however, the key idea will be to perform a nested looping of both data items and their (nested) info items. Doing that allows your algorithm to "visit" and "map" each piece of input data, to a corresponding value in the resulting array.
One way to express that would be to use nested calls to Array#reduce() to first obtaining a mapping of:
name -> {time,count}
That resulting mapping would then be passed to a call to Object.values() to transform the values of that mapping to the required array.
The inner workings of this mapping process are summarized in the documentation below:
const data=[{time:100,info:[{name:"thing1",count:3},{name:"thing2",count:2},{}]},{time:1e3,info:[{name:"thing1",count:7},{name:"thing2",count:0},{}]}];
const result =
/* Obtain array of values from outerMap reduce result */
Object.values(
/* Iterate array of data items by reduce to obtain mapping of
info.name to { time, count} value type */
data.reduce((outerMap, item) =>
/* Iterate inner info array of current item to compound
mapping of info.name to { time, count} value types */
item.info.reduce((innerMap, infoItem) => {
if(!infoItem.name) {
return innerMap
}
/* Fetch or insert new { name, info } value for result
array */
const nameInfo = innerMap[ infoItem.name ] || {
name : infoItem.name, info : []
};
/* Add { time, count } value to info array of current
{ name, info } item */
nameInfo.info.push({ count : infoItem.count, time : item.time })
/* Compound updated nameInfo into outer mapping */
return { ...innerMap, [ infoItem.name] : nameInfo }
}, outerMap),
{})
)
console.log(result)
Hope that helps!
The approach I would take would be to use an intermediate mapping object and then create the new array from that.
const data = [{time: 100, info: [{name: "thing1", count: 3}, {name: "thing2", count: 2}, {}]}, {time: 1e3, info: [{name: "thing1", count: 7}, {name: "thing2", count: 0}, {}]} ];
const infoByName = {};
// first loop through and add entries based on the name
// in the info list of each data entry. If any info entry
// is empty ignore it
data.forEach(entry => {
if (entry.info) {
entry.info.forEach(info => {
if (info.name !== undefined) {
if (!infoByName[info.name]) {
infoByName[info.name] = [];
}
infoByName[info.name].push({
time: entry.time,
count: info.count
});
}
});
}
});
// Now build the resulting list, where name is entry
// identifier
const keys = Object.keys(infoByName);
const newData = keys.map(key => {
return {
name: key,
info: infoByName[key]
};
})
// newData is the resulting list
console.log(newData);
Well, the other guy posted a much more elegant solution, but I ground this one out, so I figured may as well post it. :)
var data = [
{
time: 100,
info: [{
name: "thing1",
count: 3
}, {
name: "thing2",
count: 2
}, {
}]
},
{
time: 1000,
info: [{
name: "thing1",
count: 7
}, {
name: "thing2",
count: 0
}, {
}]
}
];
var newArr = [];
const objInArray = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o)
return true;
}
return false;
}
const getIndex = (o, a) => {
for (var i=0; i < a.length; i += 1) {
if (a[i].name === o) {
return i;
}
}
return false;
}
const getInfoObj = (t, c) => {
let tmpObj = {};
tmpObj.count = c;
tmpObj.time = t;
return tmpObj;
}
for (var i=0; i < data.length; i += 1) {
let t = data[i].time;
for (var p in data[i].info) {
if ("name" in data[i].info[p]) {
if (objInArray(data[i].info[p].name, newArr)) {
let idx = getIndex(data[i].info[p].name, newArr);
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newArr[idx].info.push(newInfoObj);
} else {
let newObj = {};
newObj.name = data[i].info[p].name;
let newInfo = [];
let newInfoObj = getInfoObj(t, data[i].info[p].count);
newInfo.push(newInfoObj);
newObj.info = newInfo;
newArr.push(newObj);
}}
}
}
console.log(newArr);
try to use Object.keys() to get the key
Here's what the data "valueContainer" looks like:
{
"totalValue": 0,
"subValues1": [
{ "value": 20 },{ "value": 30 }
],
"subValues2": [
{ "value": 10 },{ "value": 40 }
]
}
I'm trying to gather the values from 'subValues1' and 'subValues2' and store them in the 'totalValue' field? I've been trying to use computed but the for loop isn't working, here's an example of trying to accumulate values from the first sub object
computed: {
totalValue: function() {
let total;
for (let v in this.valueContainer.subValues1) {
total = total + v.value;
}
return total;
}
}
As written I'm getting NaN for the totalValue. How can I loop through the values, accumulate the values, and store them as totalValue?
Link to codepen: https://codepen.io/connorontheweb/pen/dyPvwmE
You could get the keys, filter by unwanted and iterate the values arrays. Then add the values to total.
var data = { valueContainer: { totalValue: 0, subValues1: [{ value: 20 }, { value: 30 }], subValues2: [{ value: 10 }, { value: 40 }] } };
Object
.keys(data.valueContainer)
.filter(v => v !== 'totalValue')
.forEach(k => data.valueContainer[k].forEach(({ value }) => data.valueContainer.totalValue += value));
console.log(data);
Here's a working component:
<template>
<div>{{ totalValue }}</div>
</template>
const sum = list => list.reduce((acc, value) => acc + value, 0);
export default {
data() {
return {
valueContainer: {
subValues1: [{ value: 20 }, { value: 30 }],
subValues2: [{ value: 10 }, { value: 40 }],
},
};
},
computed: {
totalValue() {
const { subValues1, subValues2 } = this.valueContainer;
const values = [...subValues1, ...subValues2].map(({ value }) => value);
return sum(values);
},
},
};
If you are using lodash, you could simplify this a bit with sumBy:
import sumBy from 'lodash/sumBy';
export default {
data() {
return {
valueContainer: {
subValues1: [{ value: 20 }, { value: 30 }],
subValues2: [{ value: 10 }, { value: 40 }],
},
};
},
computed: {
totalValue() {
const { subValues1, subValues2 } = this.valueContainer;
return sumBy([...subValues1, ...subValues2], 'value');
},
},
};
I have this following array
var array=[{ semster:1, name:Book1 }, { semster:1, name:Book2 }, { semster:2, name:Book4 }, { semster:3, name:Book5 }, { semster:3, name:Book6 }, { semster:4, name:Book7 }]
Now I want to sort my array to split the current array into chunks of array like following
var array=[[{ semster:1, name:Book1 }, { semster:1, name:Book2 }],[ { semster:2, name:Book4 }], [{ semster:3, name:Book5 }, { semster:3, name:Book6 }], [{ semster:4, name:Book7 }]]
I have tried to achieve this with following code :
function splitIntoSubArray(arr, count) {
var newArray = [];
while (arr.length > 0) {
newArray.push(arr.splice(0, count));
}
return newArray;
}
But this can only divide the array on the basis of fixed size. Any kind of suggestion is appreciated.
Thanks
You can simply use Array.reduce() to group items by semester. Object.values() on the map gives you the desired result.
var array=[{ semster:1, name:"Book1" }, { semster:1, name:"Book2" }, { semster:2, name:"Book4" }, { semster:3, name:"Book5" }, { semster:3, name:"Book6" }, { semster:4, name:"Book7" }];
var result = Object.values(array.reduce((a, curr)=>{
(a[curr.semster] = a[curr.semster] || []).push(curr);
return a;
},{}));
console.log(result);
You could reduce the array by checking the last group with the same semester.
var array = [{ semester: 1, name: 'Book1' }, { semester: 1, name: 'Book2' }, { semester: 2, name: 'Book4' }, { semester: 3, name: 'Book5' }, { semester: 3, name: 'Book6' }, { semester: 4, name: 'Book7' }],
grouped = array.reduce((r, o) => {
var last = r[r.length - 1];
if (last && last[0].semester === o.semester) {
last.push(o);
} else {
r.push([o]);
}
return r;
}, []);
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I have an array
[
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 20 },
{ price: 20 },
]
and I want it transformed into
[
{ numElements: 4, price: 10 },
{ numElements: 2, price: 20 },
]
I have tried using arr.reduce((prev, curr) => ..., []) to accomplish this, but I can't figure out how to do it.
A traditional method might use a for/loop to wrangle the data, but these days JavaScript has a number of functional methods that can help. This code uses reduce and map. To get your data in the format you want is a two stage process.
First, use reduce to create a hash table using the price as a key (because you know the each price is going to be unique:
const obj = arr.reduce((p, c) => {
// If price exists as a key its value by 1
// otherwise set it to 1.
p[c.price] = ++p[c.price] || 1;
return p;
}, {});
OUTPUT
{
"10": 4,
"20": 2
}
As it stands you've got a perfectly good object that you can access by the key/price and I would probably just stop there:
obj['10'] // 4
But if you want to get that data into the format in your question, map over the object keys to return an array of new objects.
const out = Object.keys(obj).map(key => {
return { price: +key, numElements: obj[key] };
});
DEMO
var hash = {}, result = [];
arr.forEach(function(el){
if(hash[el.price]){
hash[el.price].numElements++;
}else{
result.push(hash[el.price]={price:el.price,numElements:1});
}
});
Run
May use a hash table for price lookup. Or with reduce and find:
arr.reduce((res,{price})=>
(( res.find(el=>el.price===price) || res[res.push({price,numElements:0})-1] )
.numElements++,res)
);
Run
You can use try this:
let arr = [
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 10 },
{ price: 20 },
{ price: 20 },
]
let result = []
let counter = {}
arr.forEach( el => {
if (!counter[el.price]) counter[el.price] = 1
else counter[el.price]++
console.log(counter[el.price])
})
for (let id in counter) {
result.push({numElements: counter[id], price: id})
}
Assuming that the data comes sorted on price property, with a single .reduce() you may do as follows;
var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
result = data.reduce((r,d,i) => i ? r[r.length-1].price === d.price ? (r[r.length-1].numElemenets++, r)
: (r.push(Object.assign({}, d, {numElemenets: 1})),r)
: [Object.assign({}, d, {numElemenets: 1})], {});
console.log(result);
You could look up the price in the result array and if not found insert a new object.
var data = [{ price: 10 }, { price: 10 }, { price: 10 }, { price: 10 }, { price: 20 }, { price: 20 }],
grouped = data.reduce((r, { price }) => {
var t = r.find(p => price === p.price);
t || r.push(t = { numElements: 0, price });
t.numElements++;
return r;
}, []);
console.log(grouped);