I have an array of invoices and I want to compile an ordered list of monthlyIncome based on a value invoiceMonth (e.g. "February 2014"). The monthlyIncome array has to store the month name and the income for that month, i.e.
monthlyIncome = [
{ name: 'January 2014', income: 1000},
{ name: 'February 2014', income: 1500 }
...
];
Basically what I need is a "deeper" sort of indexOf(val), that would check if val is in a specified property of any object of monthlyIncome, and then return that index. In this example I use deepIndexOf(value, property).
for (var i=0; i<invoices.length; i++) {
var index = monthlyIncome.deepIndexOf(invoices[i].invoiceMonth, 'name');
if (index > -1) {
// month already exists in list, so add the total
monthlyIncome[index].income += invoice.total;
} else {
// month doesn't exist, so add it
monthlyIncome.push({
name: invoices[i].invoiceMonth,
income: invoices[i].total
});
}
}
The only problem is that I don't know exactly how I would write deepIndexOf. Also, I suspect there is a better way to do this in JavaScript than the way I've outlined.
Your deepIndexOf function can be like:
function deepIndexOf(array, key, value) {
var obj;
for (var idx = 0; idx < array.length; idx++) {
var obj = array[idx];
if (obj[key] === value) {
return idx;
}
}
return -1;
}
var monthlyIncome = [{
name: 'January 2014',
income: 1000
}, {
name: 'February 2014',
income: 1500
}];
console.log(deepIndexOf(monthlyIncome, 'name', 'January 2014'));
console.log(deepIndexOf(monthlyIncome, 'name', 'February 2014'));
console.log(deepIndexOf(monthlyIncome, 'name', 'None 2014'));
Or, the entire code to compile can be like:
function compile(incomeList, invoice) {
var found = false;
for (var i = 0; i < incomeList.length && !found; i++) {
if (incomeList[i].name === invoice.invoiceMonth) {
incomeList[i].income += invoice.total;
found = true;
}
}
if (!found) {
incomeList.push({
name: invoice.invoiceMonth,
income: invoice.total
});
}
}
compile(monthlyIncome, {
invoiceMonth: 'January 2014',
total: 1000
});
compile(monthlyIncome, {
invoiceMonth: 'March 2014',
total: 1000
});
You can do the following to return the first index that matches a property value in your array:
Live Demo
function indexByPropertyVal(values, propName, propVal){
var i = 0,
count = values.length;
for(; i < count; i++){
if(values[i][propName] === propVal){
return i;
}
}
return -1;
}
monthlyIncome = [
{ name: 'January 2014', income: 1000},
{ name: 'February 2014', income: 1500 },
{ name: 'March 2014', income: 1500 } ,
{ name: 'April 2014', income: 1500 } ,
{ name: 'May 2014', income: 1500 }
];
alert(indexByPropertyVal(monthlyIncome, 'name', 'April 2014'));
alert(indexByPropertyVal(monthlyIncome, 'name', 'June 2014'));
Then just update this line in your code from:
var index = monthlyIncome.deepIndexOf(invoices[i].invoiceMonth, 'name');
to
var index = indexByPropertyVal(monthlyIncome, 'name', invoices[i].invoiceMonth);
You can also augment the prototype of the Array to include the function:
Live Demo
Array.prototype.indexByPropertyVal = function(propName, propVal){
var i = 0,
count = this.length;
for(; i < count; i++){
if(this[i][propName] === propVal){
return i;
}
}
return -1;
};
Then just update this line in your code from:
var index = monthlyIncome.deepIndexOf(invoices[i].invoiceMonth, 'name');
to
var index = monthlyIncome.indexByPropertyVal('name', invoices[i].invoiceMonth);
Related
Suppose I have the following array of objects:
myArray = [ { id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 } ]
Basically my question is how to find all the id's that have the same values, then compare their dates to see which has a higher value(I am planning on converting the string with Date.parse) and finally check which has the greater percentage, and then assign a variable to the condition.
Not really sure how to go about it, but figures it looks something like the code below or not, thanks for the help in advance.
for (i = 0; i < myArray.length; i++) {
if (myArray.id[i] === myArray.id[i]) {
if (myArray.date[i] > myArray.date[i]) {
if (myArray.percentage[i] > myArray.percentage[i]) {
let stuff = stuff;
}
}
}
}
You need to remember objects with the id that you've seen earlier so you can compare them with the object you're looking at "now" in each loop iteration. A Map is a good way to do that in modern JavaScript, or an object created with Object.create(null) in ES5.
const lastSeen = new Map();
for (const entry of myArray) {
const {id, date, percentage} = entry;
const last = lastSeen.get(id);
if (last) {
if (date > last.date && percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
lastSeen.set(id, entry);
}
} else {
lastSeen.set(id, entry);
}
}
Live Example:
const myArray = [ { id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 } ];
const lastSeen = new Map()
for (const entry of myArray) {
const {id, date, percentage} = entry;
const last = lastSeen.get(id);
if (last) {
console.log(`Checking ${id} / ${date} / ${percentage}...`);
if (date > last.date && percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
console.log(`Replacing ${id}...`);
lastSeen.set(id, entry);
} else {
console.log(`Not replacing ${id}`);
}
} else {
console.log(`${id} is new, adding...`);
lastSeen.set(id, entry);
}
}
I haven't included setting stuff above because it's not clear what let stuff = stuff; in your original code was meant to do. You can find the latest ones per id in lastSeen or do something where indicated above to handle stuff.
In ES5-level code (but here in 2020 about to be 2021, I strongly recommend writing modern code and using a transpiler if you need to support obsolete environments):
var lastSeen = Object.create(null);
for (let i = 0; i < myArray.length; ++i) {
var entry = myArray[i];
var last = lastSeen[entry.id];
if (last) {
if (entry.date > last.date && entry.percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
lastSeen[entry.id] = entry;
}
} else {
lastSeen[entry.id] = entry;
}
}
Live Example:
const myArray = [ { id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 } ];
var lastSeen = Object.create(null);
for (let i = 0; i < myArray.length; ++i) {
var entry = myArray[i];
var last = lastSeen[entry.id];
if (last) {
console.log(`Checking ${entry.id} / ${entry.date} / ${entry.percentage}...`);
if (entry.date > last.date && entry.percentage > last.percentage) {
// ...this entry is newer than the previous one with the matching ID
// Replace the previous one (and possibly do something with `stuff`?)
console.log(`Replacing ${entry.id}...`);
lastSeen[entry.id] = entry;
} else {
console.log(`Not replacing ${entry.id}`);
}
} else {
console.log(`${entry.id} is new, adding...`);
lastSeen[entry.id] = entry;
}
}
You could reduce the array with an object and check if the key exist or if the wanted properties are greater.
const
data = [{ id: 'first', date: '2020-11-30', percentage: 10 }, { id: 'second', date: '2020-10-30', percentage: 20 }, { id: 'first', date: '2020-09-30', percentage: 30 }],
result = Object.values(data.reduce((r, o) => {
if (
!r[o.id] ||
r[o.id].date < o.date ||
r[o.id].date === o.date && r[o.id].percentage < o.percentage
) {
r[o.id] = o;
}
return r;
}, {}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I've got the following response from the server:
I want to get the unique key with the occurrence count.
In the following format:
0:{"name":"physics 1","count":2}
1:{"name":"chem 1","count":6}
I've already checked How to count the number of occurrences of each item in an array? but that is not I want.
Here is an es6 solution.
const data = [{
id: 0,
name: 'physics 1',
questionId: 1,
questionNr: 1
}, {
name: 'physics 1',
}, {
name: 'chem 1',
}, {
name: 'chem 1',
}, {
name: 'chem 2',
}];
const grouped = data.reduce((groups, cur) => {
const key = cur.name;
groups[key] = (groups[key] || 0) + 1;
return groups;
}, {});
const result = Object.keys(grouped).map(key => ({name: key, count: grouped[key]}));
console.log(result);
You could do it this way:
var source = [
{'section_name': 'test1'},
{'section_name': 'test2'},
{'section_name': 'test1'},
];
var temp = {};
for (var i = source.length - 1; i >= 0; i--) {
var key = source[i].section_name;
if (!temp[key]) {
temp[key] = 0;
}
temp[key] += 1;
}
var keys = Object.keys(temp);
var result = [];
for (var i = keys.length - 1; i >= 0; i--) {
var key = keys[i];
result.push({"name":key,"count":temp[key]});
}
console.log(result);
use this function this uses map and filter
t.reduce((f,l)=>{
var k=f.filter(elem=>elem.section_name==l.section_name);
if(k.length==1) k[0].count++;
else f.push({section_name:l.section_name,count:1})
return f;
},[] )
you can check this against this to verify
var t=[{section_name:"Physics"},{section_name:"Physics"},{section_name:"Chemistry"},{section_name:"Chemistry"},{section_name:"Physics"}]
I have to merge 2 array of object from rest api calls.
They are like: [{date: date, name: name}, ...]
Let's call them A array and B array.
If in the A arrays and B arrays have the same date, the final array-s object should like look this: [{date: date, nameA: nameA, nameB: nameB}]
If they dont, just insert insert and object like this: [{date: dateA, nameA: nameA}]
For example:
arrayA = [
{
date: 2017-01-01,
name: 'New Year Eve'}
},
{
date: 2017-02-02,
name: 'feb2'
}
]
arrayB = [
{
date: 2017-01-01,
name: 'New Year Eve'}
},
{
date: 2017-03-03,
name: 'march3'
}
]
The final array should look like this:
finalArray = [{
date: 2017 - 01 - 01,
nameA: 'New Year Eve',
nameB: 'New Year Eve'
},
{
date: 2017 - 02 - 02,
nameA: 'feb2'
},
{
date: 2017 - 03 - 03,
nameB: 'march3'
}
]
And the objects with shared date can be in different position in the array, so i can't check simple like
arrayA[0].date === arrayB[0].date
You could use a hash table for the reference to the objects with the same date.
var arrayA = [{ date: '2017-01-01', name: 'New Year Eve' }, { date: '2017-02-02', name: 'feb2' }],
arrayB = [{ date: '2017-01-01', name: 'New Year Eve' }, { date: '2017-03-03', name: 'march3' }],
merged = function merge(arrays, names) {
var hash = Object.create(null),
result = [];
arrays.forEach(function (a, i) {
a.forEach(function (b) {
if (!hash[b.date]) {
hash[b.date] = { date: b.date };
result.push(hash[b.date]);
}
hash[b.date][names[i]] = b.name;
});
});
return result;
}([arrayA, arrayB], ['nameA', 'nameB']);
console.log(merged);
.as-console-wrapper { max-height: 100% !important; top: 0; }
var finalArr = [];
for (var i = 0; i < arrayA.length; i++) {
for (var j = 0; j < arrayB.length; j++) {
if (arrayA[i].date == arrayB[j].date) {
//add to the final array if not exist that date
}
}
}
There is something called Array.prototype.concat() and it is used to merge 2 arrays or more, in your example it is like this:
finalArray = arrayA.concat(arrayB)
And then you have to loop over the finalArray, delete any repeated date after taking its attributes and merge to the same date like that:
for (var i = 0; i < finalArray.length; i++) {
if (finalArray[i]["date"] === finalArray[i+1]["date"] ) {
finalArray[i]["nameA"] = finalArray[i]["name"];
finalArray[i]["nameB"] = finalArray[i+1]["name"];
//delete the one that is repeated
delete finalArray[i+1][key]
}
}
Hope that works
You can use array from and subtract the length
var arrayA = [{date: '2017-01-01', name: 'New Year Eve'},{date: '2017-02-02', name: 'feb2'}];var arrayB = [{date: '2017-01-01', name: 'New Year Eve'},{date: '2017-03-03',name: 'march3'}];
var arr= Array.from(new Array(arrayA.length+arrayB.length),(x,y)=> arrayB[y]||arrayA[y-arrayA.length] );
console.log(arr);
I have an array of objects. A typical object looks like:
{
id: x
name: y
employeeInfo: {
employeeNumber: x
startDate: x
}
salary: x
}
Now I'm trying to loop through it and get the name, employeeNumber and salary.
My column variable, to be used in the loop, is:
public columns: Array<any> = [
{title: 'id', name: 'id'},
{title: 'employeeInfo.employeeNumber', name: 'employeeInfo.employeeNumber'},
{title: 'salary', name: 'salary'}]
I'm trying to loop with
item[column.name]
but of course this would result in item['emplyeeInfo.employeeNumber'], which would result in a undefined.
Can someone help?
You can split the column name and reduce, like:
column.name.split('.').reduce((res, part) => res[part], item)
split returns an array (in our case ['employeeInfo', 'employeeNumber']) so we can reduce that array using the item as the initialValue.
The reduce() method applies a function against an accumulator and each
element in the array (from left to right) to reduce it to a single
value.
Something like this:
var employees = [
{
id: 1,
name: 'Charlie',
employeeInfo: {
employeeNumber: 123,
startDate: '2017-01-23'
},
salary: 2500
},
{
id: 2,
name: 'John',
employeeInfo: {
employeeNumber: 456,
startDate: '2017-02-26'
},
salary: 3500
}
];
var columns = [
{title: 'id', name: 'id'},
{title: 'employeeInfo.employeeNumber', name: 'employeeInfo.employeeNumber'},
{title: 'salary', name: 'salary'}
];
function buildTable() {
var table = $("<table>");
var header = $("<tr>");
for(var i = 0; i < columns.length; i++) {
header.append("<th>" + columns[i].title + "</th>");
}
table.append(header);
for(var i = 0; i < employees.length; i++) {
var employee = employees[i];
var row = $("<tr>");
for(var y = 0; y < columns.length; y++) {
var properties = columns[y].name.split('.');
var value = employee;
for(var x = 0; x < properties.length; x++) {
value = value[properties[x]];
}
row.append("<td>" + value + "</td>");
}
table.append(row);
}
$("#result").append(table);
}
buildTable();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="result"></div>
Can't you just parse/split the name on the dot and for each part, you get the object, fetch its property and reiterate while there is a next property to fetch?
I ended up using a "simplify" approach. i.e. filter through the array, grab what I need, put it in a new object, put it in a temporary array and finally replace the complex array with the simple array.
try this:
var myObject = {
id: "x",
name: "y",
employeeInfo: {
employeeNumber: "inner employeeNumber",
startDate: "inner date",
},
salary: "x"
}
for(var id in myObject) {
if(typeof myObject[id] === "object") {
for(var innerId in myObject[id]){
console.log(innerId + ": " + myObject[id][innerId]);
}
} else {
console.log(id + " " + myObject[id]);
}
}
Split array of objects into new array or objects based on age value in Javascript
var items = [
{name:"Foo", age:16, color:"w"},
{name:"Bar", age:18, color:"b"},
{name:"foo", age:16, color:"w"},
{name:"bar", age:18, color:"w"},
{name:"foobar", age:18, color:"b"},
{name:"barfoo", age:20, color:"w"}
];
How can I return a list like:
var items = [
{age:16,name:"Foo"|"foo",gender:"w"|"w"},
{age:18,name:"Bar"|"bar"|"foobar",gender:"b"|"w"|"b"},
{age:20,name:"barfoo",gender:"w"}
];
I have worked but i got output with 'undefined' in name. Below is my code.
var data = [{age: 21,name: "Walter",color: "black"},{age: 25,name: "sentinel",color: "black"
},{age: 21,name: "Micah",color: "purple"},{age: 25,name: "mike",color: "black"},{age: 21,name: "Danny",color: "white"},{age: 25,name: "mike",color: "black"}];
var obj=data;
var arrayobj = obj.length;
var i, row, arr = obj, ss = {};
for (i = 0; i < arr.length; i++) {
row = arr[i];
ss[row.age] = ss[row.age] || {count: 0};
if (ss[row.age][row.age] === undefined) {
ss[row.age][row.name] = row.name;
ss[row.age]['name']+=row.name+'|';
ss[row.age]['color']+=row.color+'|';
ss[row.age]['count'] += 1;
}
}
console.table(ss);
I'm assuming you want to group the items by their age. Here is one way:
(fiddle)
items.reduce(function(buckets,item){
if(!buckets[item.age]) buckets[item.age] = [];
buckets[item.age].push(item);
return buckets;
},{});
Let's explain:
For each item, if we don't already have a 'bucket' for it, create a new empty one
Add it to the bucket
return the new updated bucket list.
The method returns an object with 3 properties: 16,18 and 20, each containing the objects with that age.
This will work. The output is in different format than one provided by exebook .
Please check and confirm. Here's a fiddle....
** UX Manager
var buckets = [];
for (var item in items) {
var currentAge = items[item].age;
if(!buckets[currentAge]) {
buckets[currentAge] = [];
for (var i in items) {
if (currentAge === items[i].age) {
buckets[currentAge].push(items[i]);
}
}
}
}
var items = [
{name:"Foo", age:16, color:"w"},
{name:"Bar", age:18, color:"b"},
{name:"foo", age:16, color:"w"},
{name:"bar", age:18, color:"w"},
{name:"foobar", age:18, color:"b"},
{name:"barfoo", age:20, color:"w"}
];
var result = [] // THIS IS THE RESULTING ARRAY THAT YOU WANT
function find(age) {
for (var i = 0; i < result.length; i++)
if (result[i].age == age) return i
return -1
}
function append(i, obj) {
result[i].name.push(obj.name)
result[i].color.push(obj.color)
}
for (var i = 0; i < items.length; i++) {
var x = find(items[i].age)
if (x < 0) result.push({ age: items[i].age, name: [items[i].name], color : [items[i].color]})
else append(x, items[i])
}
console.log(result) // PRINT THE RESULT, alternatively you can use alert(result)
The output
[ { age: 16, name: [ 'Foo', 'foo' ], color: [ 'w', 'w' ] },
{ age: 18, name: [ 'Bar', 'bar', 'foobar' ], color: [ 'b', 'w', 'b' ] },
{ age: 20, name: [ 'barfoo' ], color: [ 'w' ] } ]