Related
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
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
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.
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
Say I have two arrays:
var numbers = [1,3,5,7,9,11];
var letters = ['a','b','c'];
And say I have another reference to the numbers array:
var anotherRef = numbers;
I would like to insert one into the other in a way that modifies the existing array, so that after the operation, anotherRef === numbers.
If I didn't need to maintain the original array, I would do this:
function insert(target, source, position) {
return target.slice(0, position).concat(source, target.slice(position));
}
The problem is, after an insert with this method, the two references point at different arrays:
numbers = insert(numbers, letters, 3);
numbers !== anotherRef; // sadly, true.
If I were adding to the end of the array, I could do this, which is kind of slick:
function insertAtEnd(target, source) {
Array.prototype.push.apply(target, source);
}
Is there a nice way to insert multiple values in place in a JS array?
It looks like you're looking for the splice method:
function insert(target, source, position) {
Array.prototype.splice.apply(target, [position, 0].concat(source));
return target;
}
var numbers = [1,3,5,7,9,11];
var letters = ['a','b','c'];
insert(numbers, letters, 3);
console.log(numbers);
>> [1, 3, 5, "a", "b", "c", 7, 9, 11]
Working example: http://jsfiddle.net/0mwun0ou/1/
function insert(target, source, position) {
Array.prototype.splice.apply(target, [position,0].concat(source));
}
var numbers = [1,3,5,7,9,11];
var letters = ['a','b','c'];
var anotherRef = numbers
insert(numbers, letters, 1)
console.log(anotherRef)
I never touched anotherRef, but on that example the output is:
[1, "a", "b", "c", 3, 5, 7, 9, 11]
For an explanation how to use an array as the third argument of splice check: Is there a way to use Array.splice in javascript with the third parameter as an array?