I have 2 json files that get constantly modified. Their format is like this:
[
{
"rank": 1,
"points": 10
},
{
"rank": 15,
"points": 1601
}
]
so the amount of nested keys can be anything, in the example above there's 2.
I looped through the files and got the values I wanted from each nested key, and put them in an array.
so the array is like this const array1 = [ "1", "15" ] & const array2 = [ "18", "5" ] etc.
I now have 2 json files - arrays one showing the previous ranks (before modifying) and one showing the current ones.
What I am trying to do is, loop through the arrays and substract the ints between them, so then I can create a string that's like this: "Rank: 15 (-10)" (aka will indicate if it increased or decreased and how much. But since the array can be infinitely big , I have to create a loop that will substract array1[0] with array2[0] etc.
Don't know how, can someone help me?
Kinda confused by your question... but I'll take a stab at it. Seems as though you're just comparing the index position of one array to another, and providing some output. Could do something like the following:
const arr1 = [1, 15, 43];
const arr2 = [18, 30, 12];
const compareRanks = (oldArray, newArray) => {
oldArray.forEach((oldRank, index) => {
const newRank = newArray[index];
console.log(`Rank: ${newRank}(${oldRank - newRank})`);
});
};
compareRanks(arr1, arr2);
It's worth noting this solution is fragile. It requires both your arrays to be of equivalent size, as well as all elements are integers. If you use this, it's best you build in some guard-rails to it otherwise it will throw an error if given incorrectly structured data.
If my understanding is correct, you're case is given 2 arrays of integers, you want to the show the difference?
/* given 2 arrays (of maybe non-equal length) */
const before = [10,20,30,40,50]
const after = [5,18,35,42,55,100]
/* derive the data needed */
const shorterArray = before.length <= after.length ? before : after
const pairs = shorterArray.map((ele, idx) =>
({
points: ele,
diff: ele - after[idx]
})
)
/* print output */
pairs.map(({points, diff}=x) => // use object destructuring to separate the elements
console.log(`Rank: ${points} (${diff})`)) // print output
Related
I'm having trouble solving this problem:
I have an array of objects:
tableList = [
{table_number: 1},
{table_number: 11},
{table_number: 31}
]
I'm trying to generate a bracket list in steps of 10 up to the largest 'table_number'.
I'm using a sequence generator function for this:
// Sequence generator function (commonly referred to as "range", e.g. Clojure, PHP etc)
const range = (start, stop, step) => Array.from({ length: (stop - start) / step + 1}, (_, i) => start + (i * step));
I'm using Math.max and iterate through the array to find the biggest 'table_number':
// Search for biggest table number
var max = Math.max.apply(Math, data.map(o => o.table_number));
I then generate the bracket array with the sequencing function:
// Generate brackets up until biggest table number
var brackets = range(0, max, 10);
This gives me a bracket array of [0, 10, 20, 30].
Now the question is, how do I evaluate the array of objects to remove the empty bracket step (20) that will not contain any 'table_number'?
Or should I be generating the bracket array differently?
I would really appreciate any help for this, thanks!
Or should I be generating the bracket array differently?
If you're just after an array like [0,10,30] you can follow a different path:
tableList = [
{table_number: 1},
{table_number: 11},
{table_number: 31}
]
const tmpObj = tableList.reduce( (acc,cur) => {
acc[ Math.floor(cur.table_number/10)*10 ] = true;
return acc;
} , {})
const brackets = Object.keys(tmpObj).map((x) => parseInt(x));
console.log(brackets);
But this is taking advantage of how easy is "in steps of 10"
In case anyone stumbles on this, I have found this to be my solution.
const brackets = [...new Set(data.map(item => item.table_number - (item.table_number % 10)))]
I visit each element in the object and then subtract from it's remainder to get the numbers in steps of 10.
I then create a Set object from the outcome, effectively removing all duplicates.
I have 2 large arrays populated with strings both containing > 150.000 elements
const allNew = allArrayOneValues.filter(val => !allArrayTwoValues.includes(val));
I need to compare the two arrays like this to find out which elements are not in ArrayTwo yet or to find out which elements to delete from ArrayTwo as they are no longer in ArrayOne.
Filtering here takes around 3 to 5 minutes... is there a way to do a far more efficient compared to find out which values in ArrayOne are not yet in ArrayTwo OR which values are in ArrayTwo which are not in ArrayOne...
Thanks
Thomas
Your current algorithm is O(m*n) (where m= length of first array, n= length of second array)
Using an efficient data-structure that can do sub-linear lookup, it's possible to this in atleast O(m*lg(n))
So for 150,000 elements, it would be 10 thousand times faster and should take a few milliseconds instead of minutes.
let allArrayOneValues = [1, 2, 4]
let allArrayTwoValues = [3, 9, 2]
let hash = new Set();
allArrayTwoValues.forEach((value) => {
hash.add(value)
})
const allNew = allArrayOneValues.filter(val => !hash.has(val));
console.log(allNew)
use Set or Object may be a good choice. Here is an example:
// convert allArrayTwoValues to Object.
const tmp = allArrayTwoValues.reduce((prev, item) => { prev[item] = true; return prev; }, {});
// filter values not in allArrayTwoValues
const allNew = allArrayOneValues.filter(item => !tmp[item]);
Why I met this problem:
I tried to solve an algorithm problem and I need to return the number which appeared most of the times in an array. Like [5,4,3,2,1,1] should return 1.
And also when two number appear same time as the maximum appearance return the one came first. Like [5,5,2,2,1] return 5 because 5 appear first. I use an object to store the appearance of each number. The key is the number itself.
So When the input is [5,5,2,2,1] my object should be
Object {5: 2, 2: 2, 1: 1} but actually I got Object {1: 1, 2: 2, 5: 2}
So When I use for..in to iterate the object I got 2 returned instead of 5 . So that's why I asked this question.
This problem occurs in Chrome console and I'm not sure if this is a common issue:
When I run the following code
var a = {};
a[0]=1;
a[1]=2;
a[2]=3;
a is: Object {0: 1, 1: 2, 2: 3}
But when I reverse the order of assignment like:
var a = {};
a[2]=3;
a[1]=2;
a[0]=1;
a is also:Object {0: 1, 1: 2, 2: 3}
The numeric property automatic sorted in ascending order.
I tried prefix or postfix the numeric property like
var a = {};
a['p'+0]=1;
a['p'+1]=2;
a['p'+2]=3;
console.log(a);//Object {p0: 1, p1: 2, p2: 3}
And this keep the property order. Is this the best way to solve the problem? And is there anyway to prevent this auto sort behavior? Is this only happen in Chrome V8 JavaScript engine? Thank you in advance!
target = {}
target[' ' + key] = value // numeric key
This can prevent automatic sort of Object numeric property.
You really can't rely on order of an object fields in JavaScript, but I can suggest to use Map (ES6/ES2015 standard) if you need to preserve order of your key, value pair object. See the snippet below:
let myObject = new Map();
myObject.set('z', 33);
myObject.set('1', 100);
myObject.set('b', 3);
for (let [key, value] of myObject) {
console.log(key, value);
}
// z 33
// 1 100
// b 3
You are using a JS object, that by definition does not keep order. Think of it as a key => value map.
You should be using an array, that will keep whatever you insert on the index you inserted it into. Think of it as a list.
Also notice that you did not in fact "reverse the order of the assignment", because you inserted elements on the same index every time.
This is an old topic but it is still worth mentioning as it is hard to find a straight explanation in one-minute googling.
I recently had a coding exercise that finding the first occurrence of the least/most frequent integer in an array, it is pretty much the same as your case.
I encountered the same problem as you, having the numeric keys sorted by ASC in JavaScript object, which is not preserving the original order of elements, which is the default behavior in js.
A better way to solve this in ES6 is to use a new data type called: Map
Map can preserve the original order of elements(pairs), and also have the unique key benefit from object.
let map = new Map()
map.set(4, "first") // Map(1) {4 => "first"}
map.set(1, "second") // Map(2) {4 => "first", 1 => "second"}
map.set(2, "third") // Map(3) {4 => "first", 1 => "second", 2 => "third"}
for(let [key, value] of map) {
console.log(key, value)
}
// 4 "first"
// 1 "second"
// 2 "third"
However, using the object data type can also solve the problem, but we need the help of the input array to get back the original order of elements:
function findMostAndLeast(arr) {
let countsMap = {};
let mostFreq = 0;
let leastFreq = arr.length;
let mostFreqEl, leastFreqEl;
for (let i = 0; i < arr.length; i++) {
let el = arr[i];
// Count each occurrence
if (countsMap[el] === undefined) {
countsMap[el] = 1;
} else {
countsMap[el] += 1;
}
}
// Since the object is sorted by keys by default in JS, have to loop again the original array
for (let i = 0; i < arr.length; i++) {
const el = arr[i];
// find the least frequent
if (leastFreq > countsMap[el]) {
leastFreqEl = Number(el);
leastFreq = countsMap[el];
}
// find the most frequent
if (countsMap[el] > mostFreq) {
mostFreqEl = Number(el);
mostFreq = countsMap[el];
}
}
return {
most_frequent: mostFreqEl,
least_frequent: leastFreqEl
}
}
const testData = [6, 1, 3, 2, 4, 7, 8, 9, 10, 4, 4, 4, 10, 1, 1, 1, 1, 6, 6, 6, 6];
console.log(findMostAndLeast(testData)); // { most_frequent: 6, least_frequent: 3 }, it gets 6, 3 instead of 1, 2
To prevent the automatic sort of numeric keys of Object in Javascript, the best way is to tweak the Object keys a little bit.
We can insert an "e" in front of every key name to avoid lexicographical sorting of keys and to get the proper output slice the "e", by using the following code;
object_1 = {
"3": 11,
"2": 12,
"1": 13
}
let automaticSortedKeys = Object.keys(object_1);
console.log(automaticSortedKeys) //["1", "2", "3"]
object_2 = {
"e3": 11,
"e2": 12,
"e1": 13
}
let rawObjectKeys = Object.keys(object_2);
console.log(rawObjectKeys) //["e3", "e2", "e1"]
let properKeys = rawObjectKeys.map(function(element){
return element.slice(1)
});
console.log(properKeys) //["3", "2", "1"]
instead of generating an object like {5: 2, 2: 2, 1: 1}
generate an array to the effect of
[
{key: 5, val: 2},
{key: 2, val: 2},
{key: 1, val: 1}
]
or... keep track of the sort order in a separate value or key
I've stumbled with this issue with our normalised array which keyed with Ids> After did my research, I found out there's no way to fix using the object keys because by default the Javascript is sorting any object key with number when you iterate it.
The solution I've done and it worked for me is to put a 'sortIndex' field and used that to sort the list.
The simplest and the best way to preserve the order of the keys in the array obtained by Object.keys() is to manipulate the Object keys a little bit.
insert a "_" in front of every key name. then run the following code!
myObject = {
_a: 1,
_1: 2,
_2: 3
}
const myObjectRawKeysArray = Object.keys(myObject);
console.log(myObjectRawKeysArray)
//["_a", "_1", "_2"]
const myDesiredKeysArray = myObjectRawKeysArray.map(rawKey => {return rawKey.slice(1)});
console.log(myDesiredKeysArray)
//["a", "1", "2"]
You get the desired order in the array with just a few lines of code. hApPy CoDiNg :)
I came across this same problem, and after search a lot about that, i found out that the solution to prevent this behavior is make key as string.
Like that:
{"a": 2, "b": 2}
you can use Map() in javascript ES6 which will keep the order of the keys insertion.
just trying to solve your problem in an alternative solution, recently like to practise leetcode-like question
function solution(arr) {
const obj = {};
const record = {
value: null,
count: 0
};
for (let i = 0; i < arr.length; i++) {
let current = arr[i];
if (!obj[current]) {
obj[current] = 0;
}
obj[current]++;
if (obj[current] > record.count) {
record.value = current;
record.count = obj[current];
}
}
console.log("mode number: ", record.value);
console.log("mode number count: ", record.count);
}
simply do that while you're working with a numeric array index
data = {}
data[key] = value
What I'm Using
Angular 5
AngularFire5
Firebase & Firestore
What I'm Trying to Achieve
I am trying to cycles through all of the items in an array, which holds objects. If a certain value in the object matches the same value in the other object, then it will add another value together.
Example:
Start With:
mapData = [{"countryCode":"US", "clicks": 5}, {"countryCode":"CAN", "clicks": 9}, {"countryCode":"US", "clicks": 6}]
or
mapData = [["US", 5], ["CAN", 9], ["US", 6]]
End With:
mapDataResults = {
"US": {
"usage": 11,
"fillKey": "tens"
},
"CAN": {
"usage": 9,
"fillKey": "ones"
},
}
Where I'm Stuck
I have no idea on how to search and match through the different object values in the array. Additonally, I would need to figure out how to reorganize how the data is returned, instead of an array it would need to be in a JSON format. Finally, and I think I might be able to figure this out myself, if I can get help with the other parts, but for there to be an additonal value added "fillKey" based on how large the number of clicks (or usage) were.
Assuming your first collection is an array of arrays:
let entries = [["US", 5], ["CAN", 9],["US", 6]];
function getMostSigDigitName(number) {
switch(Math.floor(number).toString().length) {
case 1: return 'ones';
case 2: return 'tens';
}
}
function consolidateAndEmit(a) {
let result = {};
for(let i=0;i<a.length;i++) {
let key = a[i][0];
let val = a[i][1];
if(result[key]) {//exists, so update
result[key].usage += val;
}
else {//does not exist, so add
result[key] = { usage: val};
}
result[key].fillKey = getMostSigDigitName(result[key].usage);
}
return result;
}
consolidateAndEmit(entries);
If you can use lodash, this might be a good one:
mapData = [{"US", 5}, {"CAN", 9},{"US", 6}];
and then _.sumBy(mapData, 'US') or _.sumBy(mapData, "CAN")
All you need to do is map the response to an object. I have given the flow to get the sum and it gets added if mapData array has extra data containing "US" or "CAN".
Who can kindly explain the accumulator acc construction below in plain English?
return arr1.reduce(function(acc, curr){
var last = acc[acc.length-1];
if(acc.length > 0 && curr[1]===last[1]) {
last[0] += curr[0];
} else acc.push(curr);
return acc;
}, []);
}
This reduce method can be used to solve the FreeCodeCamp "Inventory Update" assignment, as part of their advanced algorithm scripting lessons.
One is required to update existing items in an inventory array (arr1) with new items in a "new delivery" array.
Two test arrays, a present inventory curInv and a new delivery newInv respectively, could be as follows:
var curInv = [
[21, "Bowling Ball"],
[2, "Dirty Sock"],
[1, "Hair Pin"],
[5, "Microphone"]
];
var newInv = [
[2, "Hair Pin"],
[3, "Half-Eaten Apple"],
[67, "Bowling Ball"],
[7, "Toothpaste"]
];
After finding several excellent articles on Javascript reduce method (such this post and a great video course on egghead.io), and somehow sensing the power it is harnassing, I would read the method as follows:
Reduce the inventory array, by creating an empty array [ ] first (the initial value), then applying the following callback function:
If the inventory array is currently not empty (has a length greater than zero), and the name of currently handled item (index 0 of curr could read "Bowling Ball" for example) is identical to the last item of the inventory array being updated, then update the amount of this item in the inventory array.
The last item is defined right above the if statement, as follows: take the present length of the accumulated array upto now, subtract 1, and use this value to index the accumulated array. The element at that index is then assigned to the variable 'last'.
On the other hand, if the inventory is empty, add the new item entirely, - that is: item name and amount.
Now return the newly accumulated array."*
How is using the length - 1 of the accumulator useful to make acc actually accumulate?
(pardon the alliteration)
I think I understand most of how this reduce method is built, but please correct me wherever I'm misreading), except this particular
use of acc.length-1.
Cheers, k.
The actual solution involves concatenating and sorting both arrays and only then reducing them.
In this case whenever we evaluate a new item, if its name is not equal to the last accumulator item, it means it's a new item.
Using your example, the list we are reducing is:
[
[ 21, 'Bowling Ball' ],
[ 67, 'Bowling Ball' ],
[ 2, 'Dirty Sock' ],
[ 1, 'Hair Pin' ],
[ 2, 'Hair Pin' ],
[ 3, 'Half-Eaten Apple' ],
[ 5, 'Microphone' ],
[ 7, 'Toothpaste' ]
]
so when we encounter the second item, the last value in the accumulator is [21, 'Boweling Ball'] and when we compare the strings we go into the first condition.
Are you asking about this section?
var last = acc[acc.length-1];
If so acc.length-1 is so because in an array
acc = [a,b,c,d]
acc.length is equal to 4
to access element d you will access it via
acc[3]; //which equals d
That is because we count positions from 0,1,2,3
tl;dr: Check out this REPL example
Reduce is capable of reducing the original array to a value that is defined on the function.
For instance, if we had an array of integers that we wanted to compute the total sum of it, we could use reduce to achieve that.
Of course, you might ask why would you ever want to use reduce as opposed to a for loop?
The primary reason why reduce is a better choice is because the reducer function that we pass as a parameter is pure. That means, there are no 'extra' nor 'external' variables that are needed in order to compute the result. In a for loop, we would need to have a totalSum variable to compute the result, whereas in the reduce version this is not needed.
Back to the OP's example, this is how we could construct the reduce to group by the invites by the same names:
// reduce example
const reducer = (accumulator, currentValue) => {
if (accumulator.length === 0) {
accumulator.push(currentValue);
return accumulator;
}
const index = accumulator.findIndex(el => el[1] === currentValue[1]);
if (index !== -1) {
accumulator[index][0] += currentValue[0];
return accumulator;
}
accumulator.push(currentValue);
return accumulator
};
Here's the equivalent version with the for loop:
// for loop example
let withForLoop = (input) => {
let res = [];
for (let i = 0; i < input.length; i++) {
let currInput = input[i];
const index = res.findIndex(el => el[1] === currInput[1]);
if (index !== -1) {
res[index][0] += currInput[0];
continue;
}
res.push(currInput);
}
return res;
}
If you are new to reduce, it might take some time until you completely understand it. Its usage almost always leads to less bug-prone applications, thus I would advocate for taking some time to fully understand its capabilities and when to use it.