Get key/value from object, perform function and send back in - javascript

I am getting [{id:'test', time: '1223'}, {id: 'test2', time: '2323'}] from the backend, I need to retrieve the time values for all, perform a function and send back into the object as [{id:'test', time: 'new value for test'}, {id: 'test2', time: 'new value for test2'}]
So I decided I would try and extract the time values and push into array using for loop but my for loop is missing something
var objext = [{id:'test', time: 'blah'}, {id: 'test2', time: 'blah'}];
var dataArray = new Array;
for (var id in objext) {
dataArray.push(objext[id]);
};
It returns the same obviously as I don't know how to select the time key.
And then I'll run my function for each as such which is working well:
var time = [23,34,56];
var map = time.map(function(num) {
if (num === 23) {
return num +2;
} else {
return num;
};
});
And then the final part will be to put back and I'm thinking I could get an array of id and one of time and then slot back in together but the code I have so far can only map one array at a time:
var data = ["Drake","Ola","d"],
result = data.map(function (a) { return { id: a }; });
I have tried using an array as such:
var data = ["Drake","Ola","d"];
var hht = [21,34,56];
result = [data,hht].map(function (a,b) { return { id: a, time:b }; });
It did not map it properly.
Any ideas?

You can perform all of the tasks withing .map() callback
var objtext = [{id:'test', time: '1223'}, {id: 'test2', time: '2323'}];
objtext = objtext.map(function(obj) {
var time = obj.time;
// do stuff with `obj.time`
if (time == 1223) {
time += 2;
obj.time = time;
}
if (time == 2323) {
time -= 2;
obj.time = time;
}
return obj
});
To create an array containing only time properties
var times = objtext.map(function({time}) {return time});

You could use an array, iterate it and use from the other parts as well for a new object.
var data = ["Drake", "Ola", "d"],
hht = [21, 34, 56],
result = data.map(function (a, i) {
return { id: a, time: hht[i] };
});
console.log(result);

You can map the array of objects and return a different time with Object.assign:
var objs = [
{id: 'Drake', time: 'blah'},
{id: 'Ola', time: 'blah'},
{id: 'd', time: 'eh' }];
var hht = [21, 34, 56];
objs = objs.map( (obj, i) => Object.assign({}, obj, { time: hht[i] }) );
console.log(objs);

Try this using for loop.
var res = [{
id: 'test',
time: '1223'
}, {
id: 'test2',
time: '2323'
}, {
id: 'test3',
time: '2453'
}],
hht = [21, 34, 56];
for (var i = 0; i < res.length; i++) {
res[i]["time"] = +res[i]["time"] + hht[i];
//or use if statements
//if(+res[i]["time"] === 23)res[i]["time"] = +res[i]["time"] + hht[i];
//scenario will be
//res[0]["time"] = 1223 + 21
//res[1]["time"] = 2323 + 34
//res[2]["time"] = 2453 + 56
}
console.log(res);

Related

Comparing two arrays and fill in missing values

I want to compare the dates from 2 arrays and push the name from the matching date inside a new array. And then push '0' for missing dates.
This is what I've tried
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
var newData = [];
var len = data.length;
for (var i = 0; i < len; i++) {
if (data[i].date == fulldate[i]) {
newData.push(data[i].name);
} else if (data[i].date != fulldate[i]) {
newData.push("0")
}
}
console.log(newData);
The problem is that it stops after encountering the unmatched date:
Amy,John,0
This is what I need
Amy, John, 0, Sara, 0
The best approach is always to use map and filter. Use map function on the fulldate The obj filters out the object (if present) with same date, as of current el value. I have used ternary operator in the return statement which returns name if object is present, and 0 otherwise.
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
var result = fulldate.map((el) => {
let obj = data.filter(item => (item.date == el))
return (obj[0]) ? obj[0].name : 0;
})
console.log(result);
On your code, you have used for-loop based on data variable length but the result has the same length with fulldate so it will be looped based on fulldate variable length.
Inside loop, you have compared fulldate[i] == data[i].date so compared only same index. Inside the loop, you need to use another loop to find the index of the matched date.
Instead of using for loop, you can simply do it using Array.prototype.map function. (Inside map function, using Array.prototype.findIndex, you can find the matched date index.)
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
const result = fulldate.map((date) => {
const existed = data.findIndex(item => item.date === date);
return existed >= 0 ? data[existed].name : 0
});
console.log(result);
var data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
var fulldate = [
'2020-01-01',
'2020-01-02',
'2020-01-03',
'2020-01-04',
'2020-01-05'
];
var newData = [];
for(var i = 0; i < fulldate.length; i++) {
var found = false;
for(var k in data) {
if(data[k].date == fulldate[i]) {
newData.push(data[k].name);
found = true;
break;
}
}
if(!found)
newData.push("0");
}
console.log(newData);
Suppose the arrays are ordered by date already, use a variable dataIdx to iterate data and Array.map() to simplify the for loop.
var data = [{name:'Amy', date:'2020-01-01'}, {name:'John', date:'2020-01-02'}, {name:'Sara', date:'2020-01-04'}];
var fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
var dataIdx = 0;
var newData = fulldate.map(date => data[dataIdx] && data[dataIdx].date == date ? data[dataIdx++].name : '0');
console.log(newData);
for variable declaration, use 'let' or 'const' instead of var. reason: var vs let vs const
And, the result you are looking for can be done using map & find js func.
const fulldate = ["2020-01-01", "2020-01-02", "2020-01-03", "2020-01-04", "2020-01-05"];
const data = [
{name: "Amy", date: "2020-01-01"},
{name: "John", date: "2020-01-02"},
{name: "Sara", date: "2020-01-04"}
];
const result = fulldate.map(date => {
const matchingDate = data.find(nameDateObj => nameDateObj['date'] === date);
return matchingDate ? matchingDate['name'] : 0;
});
console.log(result)
fyi: this can also be done using findIndex instead of find too.
fulldate.map(date => {
const matchingDateIndex = data.findIndex(nameDateObj => nameDateObj['date'] === date);
return matchingDateIndex > -1 ? data[matchingDateIndex]['name'] : 0;
});
For shortest solution you should combine map and find. Here is a oneliner:
const data = [{
name: 'Amy',
date: '2020-01-01'
}, {
name: 'John',
date: '2020-01-02'
}, {
name: 'Sara',
date: '2020-01-04'
}];
const fulldate = ['2020-01-01', '2020-01-02', '2020-01-03', '2020-01-04', '2020-01-05'];
const result = fulldate.map(x => (data.find(y => y.date === x) || {name: 0}).name)
console.log(result)

How to invert the structure of nested array of objects in Javascript?

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

How to convert an unorganized array into an grouped array by id

I'm trying to create an array that contains objects with an id and amount, grouped by id. The ids needs to be unique. So if there is 2 objects with same id, the amount will be added.
I can do it with nested for-loops, but I find this solution inelegant and huge. Is there a more efficient or cleaner way of doing it?
var bigArray = [];
// big Array has is the source, it has all the objects
// let's give it 4 sample objects
var object1 = {
id: 1,
amount: 50
}
var object2 = {
id: 2,
amount: 50
}
var object3 = {
id: 1,
amount: 150
}
var object4 = {
id: 2,
amount:100
}
bigArray.push(object1,object2,object3,object4);
// organizedArray is the array that has unique ids with added sum. this is what I'm trying to get
var organizedArray = [];
organizedArray.push(object1);
for(var i = 1; i < bigArray.length; i++ ) {
// a boolean to keep track whether the object was added
var added = false;
for (var j = 0; j < organizedArray.length; j++){
if (organizedArray[j].id === bigArray[i].id) {
organizedArray[j].amount += bigArray[i].amount;
added = true;
}
}
if (!added){
// it has object with new id, push it to the array
organizedArray.push(bigArray[i]);
}
}
console.log(organizedArray);
You can definitly make it cleaner and shorter by using reduce, not sure about efficiency though, i would say a traditional for loop is more efficient :
var bigArray = [];
var object1 = {id: 1, amount: 50}
var object2 = {id: 2, amount: 50}
var object3 = {id: 1, amount: 150}
var object4 = {id: 2, amount: 100}
bigArray.push(object1, object2, object3, object4);
var organizedArray = bigArray.reduce((acc, curr) => {
// check if the object is in the accumulator
const ndx = acc.findIndex(e => e.id === curr.id);
if(ndx > -1) // add the amount if it exists
acc[ndx].amount += curr.amount;
else // push the object to the array if doesn't
acc.push(curr);
return acc;
}, []);
console.log(organizedArray)
Rather than an organized array, how about a single object whose keys are the ids and values are the sums.
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
If you really need to convert this to an array of objects then you can map the keys to objects of your choosing like this:
var bigArray = [
{ id: 1, amount: 50 },
{ id: 2, amount: 50 },
{ id: 1, amount: 150 },
{ id: 2, amount: 100 }
];
let total = {}
bigArray.forEach(obj => {
total[obj.id] = (total[obj.id] || 0) + obj.amount;
});
console.log(total);
// If you need the organized array:
let organizedArray = Object.keys(total).map(key => ({ id: key, amount: total[key] }));
console.log(organizedArray);
function getUniqueSums(array) {
const uniqueElements = [];
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const id = element.id;
const uniqueElement = findElementByPropertyValue(uniqueElements, 'id', id);
if (uniqueElement !== null) {
uniqueElement.amount += element.amount;
continue;
}
uniqueElements.push(element);
}
return uniqueElements;
}
function findElementByPropertyValue(array, property, expectedValue) {
const arrayLength = array.length;
for(let index = 0; index < arrayLength; index++) {
const element = array[index];
const value = element[property];
if (value !== expectedValue) {
continue;
}
return element;
}
return null;
}
This is an untested code. You will be able to understand the logic. Logic is almost same yours. But, perhaps a more readable code.

how to count duplicate values object to be a value of object

how to count the value of object in new object values
lets say that i have json like this :
let data = [{
no: 3,
name: 'drink'
},
{
no: 90,
name: 'eat'
},
{
no: 20,
name: 'swim'
}
];
if i have the user pick no in arrays : [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
so the output should be an array
[
{
num: 3,
total: 11
},
{
num: 90,
total: 1
},
{
num:20,
total: 4
}
];
I would like to know how to do this with a for/of loop
Here is the code I've attempted:
let obj = [];
for (i of arr){
for (j of data){
let innerObj={};
innerObj.num = i
obj.push(innerObj)
}
}
const data = [{"no":3,"name":"drink"},{"no":90,"name":"eat"},{"no":20,"name":"swim"}];
const arr = [3,3,3,3,3,3,3,3,3,3,3,20,20,20,20,80,80];
const lookup = {};
// Loop over the duplicate array and create an
// object that contains the totals
for (let el of arr) {
// If the key doesn't exist set it to zero,
// otherwise add 1 to it
lookup[el] = (lookup[el] || 0) + 1;
}
const out = [];
// Then loop over the data updating the objects
// with the totals found in the lookup object
for (let obj of data) {
lookup[obj.no] && out.push({
no: obj.no,
total: lookup[obj.no]
});
}
document.querySelector('#lookup').textContent = JSON.stringify(lookup, null, 2);
document.querySelector('#out').textContent = JSON.stringify(out, null, 2);
<h3>Lookup output</h3>
<pre id="lookup"></pre>
<h3>Main output</h3>
<pre id="out"></pre>
Perhaps something like this? You can map the existing data array and attach filtered array counts to each array object.
let data = [
{
no: 3,
name: 'drink'
},
{
no:90,
name: 'eat'
},
{
no:20,
name: 'swim'
}
]
const test = [3,3,3,3,3,3,3,3,3,3,3,90,20,20,20,20]
const result = data.map((item) => {
return {
num: item.no,
total: test.filter(i => i === item.no).length // filters number array and then checks length
}
})
You can check next approach using a single for/of loop. But first I have to create a Set with valid ids, so I can discard noise data from the test array:
const data = [
{no: 3, name: 'drink'},
{no: 90, name: 'eat'},
{no: 20, name: 'swim'}
];
const userArr = [3,3,3,3,3,3,3,3,7,7,9,9,3,3,3,90,20,20,20,20];
let ids = new Set(data.map(x => x.no));
let newArr = [];
for (i of userArr)
{
let found = newArr.findIndex(x => x.num === i)
if (found >= 0)
newArr[found].total += 1;
else
ids.has(i) && newArr.push({num: i, total: 1});
}
console.log(newArr);

Stable sort dynamically created array which contains duplicates (javascript)

I've been working on this for a couple of days, looked through all the other threads but can't seem to find a solution to my problem.
I start off with an object array, each specifying a 'start' and 'end' number. I loop through the difference between these, creating a new object from each number in between, storing the 'name' data in each new object created.
After which, I would like to sort the data based off the number, but maintaining the placement order of the original array.
const data = [{
'start': 10,
'finish': 14,
'name': 'one'
}, {
'start': 14,
'finish': 19,
'name': 'two'
}, {
'start': 12,
'finish': 16,
'name': 'three'
}]
let newData = [];
for (let d of data) {
const start = d.start;
const finish = d.finish;
for (let i = start; i <= finish; i++) {
newData.push({
'number': i,
'name': d.name
})
}
}
const sortData = (data) => newData.sort((a, b) => a.number - b.number)
const result = sortData(data)
// Test order for number 14
for(let r of result){
if(r['number'] == 14){
console.log(r.name)
}
}
// Show all data
console.log(result)
Sorry for the long snippet, but it's difficult for me to display the problem.
At the moment, when looking for the items with the number '14' after the sort, the names displayed are 'two, three, one'. I would like to instead maintain the original order of the 'data' array, so it should display as 'one, two, three'.
I'm not sure how sort() is deciding what goes where, because if I change the second object's finish number to 17 inside the original data array it will display the items for number '14' in the correct order.
Just to repeat - I would like to sort the newData array by the number but placed in the order of the original 'data' array.
I hope everything is clear! Please let me know if I can improve the question some how.
Here is a plunkr with the code - http://plnkr.co/edit/tM0h4C93CAnyy1g0T3jp?p=preview.
For a stable sort, you need another propery, like the index of the array.
With a new property,
const data = [{ start: 10, finish: 14, name: 'one' }, { start: 14, finish: 19, name: 'two' }, { start: 12, finish: 16, name: 'three' }]
let newData = [];
data.forEach((d, pos) => {
const start = d.start;
const finish = d.finish;
for (let i = start; i <= finish; i++) {
newData.push({ number: i, name: d.name, pos })
}
});
const sortData = (data) => newData.sort((a, b) => a.number - b.number || a.pos - b.pos)
const result = sortData(data)
// Test order for number 14
for(let r of result){
if(r['number'] == 14){
console.log(r.name)
}
}
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }
or sort by name with an object.
const data = [{ start: 10, finish: 14, name: 'one' }, { start: 14, finish: 19, name: 'two' }, { start: 12, finish: 16, name: 'three' }]
let newData = [];
let order = Object.create(null);
data.forEach((d, pos) => {
const start = d.start;
const finish = d.finish;
order[d.name] = pos;
for (let i = start; i <= finish; i++) {
newData.push({ number: i, name: d.name })
}
});
const sortData = (data) => newData.sort((a, b) => a.number - b.number || order[a.name] - order[b.name])
const result = sortData(data)
// Test order for number 14
for(let r of result){
if(r['number'] == 14){
console.log(r.name)
}
}
console.log(result)
.as-console-wrapper { max-height: 100% !important; top: 0; }

Categories