Reduce arrays in objects that in big array JS - javascript

I have a nested array, consisting of a lot of objects (400 items). That objects have an ID property and an incomes property. That incomes property is an array of objects (40 items) that have value and date.Question is, how can I iterate through that main array to sum all my small array values to one summary?
[{id: 1-400, incomes:[{value: "String", date: "String"}]}]
And, for example, that summary will be the 3rd property of main array:
[{id: 1-400, incomes:[{value: "String", date: "String"}], summaryIncome: "number"}]
I can iterate for that 400 items with for in loop, but I don`t understand how can I iterate both for big 400 items and small 40 items arrays with one loop.
for(let i in data){
data[i].incomes[i].value
}
So i will be >40 and I`ll get undefined in incomes[i].value. Please help :)

var a = [
{id: 1, incomes: [{value:1}, {value: 2}]},
{id: 2, incomes: [{value:2}, {value: 3}]}];
var b = a.map(item => ({
...item,
summaryIncome: item.incomes.reduce((t, income) =>
t += income.value, 0)
}));
console.log(b);
This should print:
{id: 1, incomes: Array(2), summaryIncome: 3}
{id: 2, incomes: Array(2), summaryIncome: 5}

Sum Array of Objects
Iterate over each object and calculate the sum, assigning it to a new key:
let data = getData()
data.forEach(obj => {
obj.summaryIncome = obj.incomes.reduce((agg,{value}) => agg+=value, 0);
})
console.log(data);
function getData(){
return [
{id: 1, incomes: [{value:1, date:new Date()}, {value: 2, date:new Date()}]},
{id: 2, incomes: [{value:3, date:new Date()}, {value: 4, date:new Date()}]}
];
}
This assumes:
source data has a value
the source data is mutable (can store summaryIncome with it)
there is always an incomes and value key
it uses reduce to iterate over each value and collect an aggregate, agg, which will hold the item's sum
your browser supports ES6 or you have a transpiler for polyfills
Your Issues
400 items is not a lot of items. You should be able to easily iterate them; however, your number of items and the number of values are not a uniform array; it's not a 400x400, it's 400x40. You don't have the same number of ids as you do values for each id. So your i is fine for your item loop, but because you have fewer values than items, you will reach an end.
Even if they were the same number, it doesn't seem there is anything that binds the observation to the number of incomes, so it would be strongly discouraged to only use one loop and/or variable for tracking the index.
For an example of an implementation that uses two loops, refer to the previous section, which uses forEach and reduce to iterate both dimensions of your data. Likewise, below modifies your original code that to demonstrate using two for-loops:
for(let item of data){
let sum = 0;
for(let {value} of item.incomes){
sum += value;
}
item.summaryIncome = sum
}

Related

Understanding React's setState method

I understand the below code adds a new object to an array of objects but fuzzy on a specific syntax: setProductList([array, obj])
From what I'm seeing, the setProductList function takes in an object. That object consists of an array and an object. So how does it add the object to the array? is this built into JS or React?
// array of products
const Products = [
{
item: "basketball",
price: 3,
description: "basketball desc",
},
{
item: "football",
price: 5,
description: "football desc",
},
];
// setting state
const [productList, setProductList] = useState(Products);
// handling click
const handleClick = () => {
// Don't quite understand the syntax of below block
setProductList([
...productList, // spread creates a copy of the array
{
item: "baseball",
price: 4,
description: "baseball desc",
},
]);
};
What you are seeing is Spread operator (...)
Spread syntax can be used when all elements from an object or array need to be included in a list of some kind.
By writing [ (element inside) ], you did create a new array. ...productList alone does not create a new array
You could understand ...productList as it helps you write shorter code, instead of writing like this
setProductList([
productList[0],
productList[1],
// ...
productList[productList.length - 1],
{
item: "baseball",
price: 4,
description: "baseball desc",
},
])
Another example that could help you understand the spread operator, is the use of Math.max. Basically, syntax of Math.max is Math.max(value0, value1, ... , valueN).
For example you have an array of N elements, because Math.max solely could not take an array as arguments, so to calculate max of the array, you could iterate each element and compare
const arr = [1, 3, 2]
let max = -Infinity
for (const num of arr) {
max = Math.max(max, num)
}
console.log(max)
But now with spread operator, you could achieve the same result in a shorter way
const arr = [1, 3, 2]
let max = Math.max(...arr)
console.log(max)
This feature is built into js and called Object Spreading
Example
var x = [1, 2] // an array
console.log(x)
// spreads an array, and put an extra element
var y = [...x, 6]
console.log(y)
so, the state hook returns a function here - setProductList
That just updates the state to the new array.
it's a common practise. See this detailed answer for in deth explanation
Ok, the code which you don't understand basically is setting a new copy of the current array, BUT including the new element (it'd be like push a new element in the array. BUT push would modify the original array. In this case, it's making a copy and pushing the value to that new array)
Lets go line by line.
We have:
setProductList([
...productList,
{
item: "baseball",
price: 4,
description: "baseball desc",
},
]);
We will define:
setProductList([ // Line 0
...productList, // Line 1
{ // Line 2
item: "baseball", // Part of Line 2
price: 4, // Part of Line 2
description: "baseball desc", // Part of Line 2
}, // Part of Line 2
]); // Part of Line 1(]) & 0())
With Line 0 (setProductList) we are setting a new value to productsList because react hooks and useState. (https://reactjs.org/docs/hooks-state.html)
With Line 1 we are creating a new copy of the array ([...oldArray]) will return a copy of the previous array (You can read: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
With Line 2, we are including a new value at the end of the new array.
I guess your confusion comes from not understanding the spread syntax.
Spread in array literals
A more powerful array literal
Without spread syntax, to create a new array using an existing array
as one part of it, the array literal syntax is no longer sufficient
and imperative code must be used instead using a combination of
push(), splice(), concat(), etc. With spread
syntax this becomes much more succinct:
let parts = ['shoulders', 'knees'];
let lyrics = ['head', ...parts, 'and', 'toes'];
// ["head", "shoulders", "knees", "and", "toes"]
Just like spread for argument lists, ... can be used anywhere in the
array literal, and may be used more than once.
When looking at your code:
setProductList([
...productList, // spread creates a copy of the array
{
item: "baseball",
price: 4,
description: "baseball desc",
},
]);
The statement "spread creates a copy of the array" is incorrect. The spread operator by itself does not create a copy. It takes the items in an iterable (like an array) and and places them in the context as separate arguments/elements.
[...productList]
The above would create a copy because we define a new array and place all the items of productList in this new array. Thus creating a copy.
[...productList, newItem]
Defines a new array, places all the items of productList in this new array and places newItem behind it. Since we added newItem it strictly speaking is not really a copy.

Array of objects - return objects by order & return average ranking

This is a practice hw question.
INSTRUCTIONS
You have an array of objects in JavaScript. Each one contains a name (a string) and ranking (a number).
Write two functions, one to return the objects ordered by ranking and another to return the average ranking.
I have it sorted, but need to find the average ranking as well.
CODE
let obj = [{name: "bob", ranking: 2}, {name: "rob", ranking: 3}, {name: "cob", ranking: 1}];
const output = obj
.sort((a, b) => a - b);
function avgRanking(num) {
let sum = obj.reduce((previous, current) => current += previous);
let avg = sum / obj.ranking.length;
return avg
}
console.log(output);
console.log(avgRanking()); // NaN
What am I doing wrong?
.sort() sorts an array, and only that. If you start with an array of objects and call .sort() on it, the array will still be an array of objects, only (possibly) in a different order.
If you want a sorted array of just the rankings, you need to .map to extract the values (either before or after sorting). You should also probably use a better variable name for the array - it's an array, not a plain object, so perhaps name it arr or users (or something that more accurately describes what the collection is), else there's a good chance of confusion:
const users = [{name: "bob", ranking: 2}, {name: "rob", ranking: 3}, {name: "cob", ranking: 1}];
const output = users
.map(obj => obj.ranking)
.sort((a, b) => a - b);
console.log(output);

Merge two arrays of objects with separate ids, so that the objects don't duplicate

I have two arrays of objects, each object has .id property. I need to merge them so that in the return array, each object id is unique (object whos .id is already present in other object in that array, when trying to join the array is discarded). Preferably es6 way. Thanks!
var a = [{id: 2}, {id:3}]
var b = [{id: 4}, { id:3}]
mergeArrays(a,b)
// should return
////[{id: 2}, {id:3}, {id: 4}]`
Here's one approach: concatenate the two arrays into one and reduce them into an object by id which removes duplicates. Grabbing an array of values with Object.values() produces the desired result:
var a = [{id: 2}, {id:3}]
var b = [{id: 4}, {id:3}]
const result = Object.values(a.concat(b).reduce((a, e) => {
a[e.id] = e;
return a;
}, {}));
console.log(result);
Note that b's keys will overwrite any duplicate keys from a because of the order of the concatenation.

Get difference between arrays with same objects

I have two arrays, like this:
var a = [
{id: 1}, {id: 2}, {id: 1}, {id: 3}
];
var b = [
{id: 1}, {id: 3}
];
I want to get the elements that array a has and array b doesn't. The expected outcome is:
[
{id: 1}, {id: 2}
]
I tried this:
a.filter(x => b.indexOf(x) == -1);
And this:
a.filter(x => new Set(b).has(x) == false);
The problem with those two is that it treats {id: 2} from array A and {id: 2} from array B as different objects, so those two lines of code simply return the full array A.
Another difficulty, I need {id: 1} and {id: 1} to be treated as two different objects, even if they have the exact same properties and values inside.
In my actual code, I have objects which are more complex and have more properties, but the situation is the same.
You could take a set and return the filtered array without the values of the set's id.
var a = [{ id: 1 }, { id: 2 }, { id: 1 }, { id: 3 }],
b = [{ id: 2 }, { id: 3 }],
s = new Set(b.map(({ id }) => id)),
result = a.filter(({ id }) => !s.has(id));
console.log(result);
I eventually got this working:
function differenceOf(arr1, arr2) {
var differences = $.extend(true, [], arr1); // creates clone
var arr2Duplicate = $.extend(true, [], arr2);
arr2Loop:
for(var i = 0; i < arr2Duplicate.length; i++) {
var obj2 = arr2Duplicate[i];
if(obj2 == null) continue;
differencesLoop:
for(var j = 0; j < differences.length; j++) {
var obj1 = differences[j];
if(obj1 == null) continue;
if(obj1.id == obj2.id) {
differences.splice(j, 1);
arr2Duplicate.splice(i, 1);
i = -1;
j = -1;
break differencesLoop;
}
}
}
return differences;
}
I cloned the two arrays for future manipulation, so references would be removed and the original arrays wouldn't be affected. I set the first array to be the differences array, so I can delete the elements that appear in the other array.
I iterate through the second array and then inside that loop I iterate through the first array. Then, I check for equal ID's; if so, then I found an element that is in both arrays, so I simply remove it from the first array. I also remove the element from the second array to prevent duplicate comparison, and then I break out of the loop to prevent more deletion of elements with the same ID.
When I remove the elements, the loop is still going, and eventually it'll reach that empty slot where the element used to be, so I check if it's null; if so, skip and keep going.
After both loops finish, I'm left with an array that has the elements that are different, regardless of elements that have the same properties.
EDIT: I changed the jQuery each loops to standard for loops because when I tried to break out of the inner loop, it broke out of the outer loop as well. I fixed this by adding those GOTO labels, which fixed the breaking problem.
When I detected a duplicate, I also reset the indices back to -1, because when the loop continues, the index will increment and skip over objects, leading to incorrect data. I reset it to -1 so that when the code block finishes, it'll increment back to 0 and it'll scan the arrays over again.

Using JavaScript to compare 2 arrays and create a new array of objects which does not contains the objects that match by id field

I have two arrays of objects. I would like to use JavaScript to create a new array which does not contains object that match by id field.
First array
[{id: 1, name: "Jo"},{id: 2, name: "Pat"},{id: 3, name: "Mike"}]
Second array
[{id: 1, name: "Jo"},{id: 2, name: "Pat"},{id: 3, name: "Mike"}, {id: 4, name: "Mitch"}, {id: 5, name: "Karl"}]
should return this array
[{id: 4, name: "Mitch"}, {id: 5, name: "Karl"}]
Using lodash would be better if possible. Please note that the first array is always smaller or equal to the second one and can only contain objects that are in the second one.
So I tried the following...
let newArray = _.reject(secondArray, {'id': firstArray});
Without success, I need help with the syntax.
Many thanks for your time and help.
Short and sweet. (Assumes one holds the first array and two holds the second array)
var oneIDs = one.map(function (a) {return a.id});
var result = two.filter(function (a) {
return oneIDs.indexOf(a.id) === -1;
});
Explanation:
First, get a lookup array of all the id's of the elements in the first array.
Second, get all the elements of the second array whose id's are not in the lookup array (oneIDs).

Categories