extension of this question
So I was playing around with a project and I found myself needing to create a string where I didn't know the exact order of the lettering right away, but I did know their position(and by extension, the length of this string).
Part of the reason for doing it this way is that I don't want to skip array elements at any point during the process(NO ["a", , "t"] during the process). Meaning that the order of insertion matters.
let string_length = 10;
let o = {
0: "a",
4: "q",
7: "t",
3: "p",
6: "h",
1: "z",
2: "t",
5: "a",
9: "b",
8: "z"
};
function obj_to_arr(o) {
// this returns ["a","z","t","p","q","a","h","t","z","b"]
}
All the answers I've found until now don't necessarily guarantee that given an index object, it will give the corresponding ordered object, and this is mainly because of the nature of object, being unordered.
Is there a way to implement this?
You could fix this in two possible ways (and probably more).
Array
Either you add objects containing both the key and the value to an array.
array.push({c: "a"}) or array.push({ key: c, value: "a"}), then add the next and so on.
Push puts an element at the end of an array.
Sorted object
Or you insert into the object using alphabetic keys, in which case you can use the example below.
You could sort the keys (properties) of the object, and use them to access each value and put them into another array. Here's a verbose example of how you could do that.
const someObject = {
c: "a",
4: "q",
7: "t",
b: "p",
6: "h",
1: "z",
2: "t",
5: "a",
a: "b",
8: "z"
};
function obj_to_arr(obj) {
let result = []
// Get keys from object as an array
let keys = Object.keys(obj)
console.log('keys: ', keys)
// Sort the array in alphabetical order
let sorted = keys.sort()
console.log('sorted: ', sorted)
// Add each value to the resulting array
sorted.forEach(function(key) {
console.log('adding ' + obj[key] + ' to array')
result.push(obj[key])
})
console.log('result: ', result)
return result
}
// Call the function
obj_to_arr(someObject)
Using Object.keys(), you can retrieve an array of keys, sort them numerically, then map that sorted array to one with the values.
The result will be an array of values in an order corresponding to the numeric values of the keys, even if they are not sequential.
For example
let o = {"0":"a","1":"z","2":"t","3":"p","4":"q","5":"a","6":"h","7":"t","8":"z","10":"b"}
let sortedValues = Object.keys(o)
.sort((a, b) => a - b) // numeric comparison
.map(k => o[k]) // map to values
console.info(sortedValues)
The short answer is "you can't" because of the Javascript spec.
Objects in Javascript are not guaranteed to have their keys ordered in any way due it being a primitive type that engines are allowed to optimize the hell out of, including reordering keys to speed up property access.
If ordering really matters, then you shouldn't be using an object primitive. Either an array of tuples (which has guaranteed ordering):
let fixedList = [
{ key: 0, value: 'a' },
{ key: 4, vlaue: 'q' },
...
];
or you can use a Map, which exists specifically as a way to have "an object, with the same ordering of key/value pairs as you declared".
Of the two, for data transport you probably want the former, and for running code you typically want the latter.
Related
So here's what I'm trying to do, I have an application I've been working on that allows users to read articles and take quizzes on them, each article is contained in a category. Category has an _id which is unique. Quiz attempts are held within another table which refrences the article, which refrences the category.
I have looped through all of the quiz attempts and store their keys and values in an array of objects which looks like the following:
const userScoreArray = [];
for(let i = 0; i < data[1].length; i++) {
userScoreArray.push({[data[1][i]['dataValues']['article_id']]: data[1][i]['dataValues']['score'] }) // overall
}
Now I have an array which stores quiz attempts for each category:
[
{4: 5},
{4: 1},
{3: 6},
{5: 0}
// { category: score }
]
How would I be able to get into this array, select all objects with the key of "4" and and then add all of their values together, and then again grab all objects with the key of "5" and add their values together? I was thinking of using a loop to do it but my brain starts to steam right then.
You can use an Array.reduce iterator, then Object.keys() and Object.values() to extract the numbers for comparing and adding
let data = [
{4: 5},
{4: 1},
{3: 6},
{5: 0}
// { category: score }
];
const getScoreFor = n => {
return data.reduce((b, a) => {
return b + (Object.keys(a)[0] == n ? Object.values(a)[0] : 0);
}, 0);
}
console.log(getScoreFor(4))
You can use the reduce method as follows:
const array = [{ 4: 5 }, { 4: 1 }, { 3: 6 }, { 5: 0 }];
const accArray = array.reduce((acc, obj) => {
if (obj[4] ?? obj[5]) {
acc += Object.values(obj)[0];
}
return acc;
}, 0);
console.log(accArray); // 6
MDN ref.:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce
By using reduce(), you loop through the array having access at each iteration to an accumulator, the current element and the current index. The initial value of the accumulator, on the above example, is set to zero; at each iteration, we check if the object has a key equal to 4 or 5; if the condition is true we add to the accumulator the value of that object, then it is returned the accumulator (because that is how the reduce method works).
OBS: I used the nullish coalescing operator (??) just in case you need to use 0 as key ({0: any_value}), as the or operator (||) works with falsy values.
i am trying to understand array sorting method , the problem i am currently facing is when i am declaring some variables inside compare function its not sorting the same as it is doing without those variables although those variables are not used anywhere
can anyone explain what is actually happening here
also i find out that sort functions behave different in firefox and chrome
page_link i am testing this in firefox dev edition
let list1 = ["a","b","c","d","e","f","g","h","i"]
list1.sort((a,b)=>{
let pat = ["d","a"]
return b - a
})
console.log(list1) // Array(9) [ "a", "b", "c", "d", "e", "f", "g", "h", "i" ]
let list2 = ["a","b","c","d","e","f","g","h","i"]
list2.sort((a,b)=>{
// let pat = ["d","a"]
return b - a
})
console.log(list2) // Array(9) [ "i", "h", "g", "f", "e", "d", "c", "b", "a" ]
If you do "a" - "b" it evaluates to NaN which is incorrect as that's not what you intended and also inconsistent (varies browser to browser).
Either don't pass a callback to sort in which case it does the following:
The sort() method sorts the elements of an array in place and returns the sorted array. The default sort order is ascending, built upon converting the elements into strings, then comparing their sequences of UTF-16 code units values.
Or you can use String.prototype.localeCompare
let list = ["a", "b", "c", "d", "e", "f", "g", "h", "i"];
console.log([...list].sort());
console.log([...list].sort((a, b) => a.localeCompare(b)));
If you want to prepend some values to the sorted array you'll have to do it separately you can't do it using sort.
let list = ["a", "b", "c", "d", "e", "f", "g", "h", "i"];
let pat = ["x", "y"];
let sortedList = [...list].sort();
let patAppendedSortedList = [...pat, ...sortedList];
console.log(patAppendedSortedList);
And if you want to sort both the list and pat but you want to keep all pat elements before all list elements then consider the snippet below.
let list = ["a", "b", "c", "d", "e", "f", "g", "h", "i"];
let pat = ["z", "x", "y"];
let patSet = new Set(pat);
let sortedList = [...pat, ...list].sort((a, b) => {
// If a has higher priority put it before b
if (patSet.has(a) && !patSet.has(b)) {
return -1;
}
// If b has higher priority put it before a
if (patSet.has(b) && !patSet.has(a)) {
return 1;
}
// Otherwise both a and b have same priority
// Sort them according to their value
return a.localeCompare(b);
});
console.log(sortedList);
If you subtract a string from another string, it will attempt to convert the strings to numbers.
All of your strings will convert to NaN.
NaN-NaN is also NaN.
So it doesn't matter which two values from your array you are comparing, your comparison function will always return NaN.
(Note that a comparison function is supposed to return a number that is 0, greater than 0 or less than 0 and be consistent for any given pair of values. The one you are using is just broken for the data you are using).
The order the letters get sorted into therefore depends on the order in which they are compared to each other (because your comparison function is broken).
That order is determined by the sort algorithm that the JS engine uses. This is an implementation detail that the specification doesn't mandate.
One browser might use a quick sort while another might use a bubble sort. Hence you get different results.
Write a comparison function which isn't nonsense to get consistent results.
The default comparison function is find for lexical sorting under most circumstances.
Now i understood your goal. But you cant archive this with your way. You cant make some prepends of values in the JS sort function. You have to make it in two steps. 1.) sort 2.) prepend your pat values to the sorted array.
const list = ["a","b","c","d","e","f","g","h","i"];
const pat = ["d","a"];
const sorted = [...pat, ...list.sort()];
console.log(sorted)
Hello there I am trying to do a code wars kata: https://www.codewars.com/kata/54a91a4883a7de5d7800009c/train/javascript
I know what I have done is very long-winded but I wanted to do it step by step before I refactored it.
I have split the string input from the user
Filtered through the array just to get the number and converted it to a NUMBER
Incremented that number by 1
Changed number back to string
Now I want to add my string number to the original array but it doesn't work and I don't understand why :S
I know this is not the final answer to the kata but I am just trying things and wondered why this did not work...
function isNumeric(num){
return !isNaN(num)
}
function incrementString (string) {
const splitString = string.split("");
let numbers = Number(splitString.filter(el => isNumeric(el)).join("")); //[
'f', 'o', 'o', 'b',
'a', 'r', '0', '0',
'4', '2'
]
let incrementNumber = numbers +=1; // 43
let revertNumberToString = incrementNumber.toString(); // "43"
let test = splitString.push(revertNumberToString); // why can I not push the number 43 onto my original array?
console.log(test); // 11? why?
}
incrementString("foobar0042")
It does seem to be working correctly. If you check splitString again after you push to it then it will have all 11 items. That is where the number 11 is coming from. When you save a push to a variable it doesn't make a new array but rather it saves the length of the new array.
console.log(splitString)
// ["f", "o", "o", "b", "a", "r", "0", "0", "4", "2"]
let test = splitString.push(revertNumberToString);
console.log(splitString)
// ["f", "o", "o", "b", "a", "r", "0", "0", "4", "2", 43]
console.log(test); // 11? why?
Javascript push method adds the element to the array and returns the length, that's why you get 11 instead of the array itself. Reference
You could take a different approach by splitting the value into string and number part and take the length of the number part for later padding the value with leading zeroes.
function incrementString(value) {
const
string = (value.match(/\D+/) || [''])[0],
number = (value.match(/\d+/) || ['0'])[0];
return string + (+number + 1).toString().padStart(number.length, 0);
}
function assertEquals(a, b) {
console.log(a === b, a, b);
}
assertEquals(incrementString("foobar000"), "foobar001");
assertEquals(incrementString("foo"), "foo1");
assertEquals(incrementString("foobar001"), "foobar002");
assertEquals(incrementString("foobar99"), "foobar100");
assertEquals(incrementString("foobar099"), "foobar100");
assertEquals(incrementString(""), "1");
I have checked this page : mozilla documentation
I dont understand why the index 0 with :
const object3 = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(object3)[0]);
// expected output: Array ["100", "a"] <== i thought of this
instead the documentation says you get :
// expected output: Array ["2", "b"]
Someone can explain it why ?
The Docs say that Object.entries returns an array of given objects enumerable property [key,value] pairs . So yes its confusing if you look at this statement
const object3 = { 100: 'a', 2: 'b', 7: 'c' };
and end up getting ["2", "b"] when you call Object.entries(object3)[0].
When you are doing this Object.entries(object3)[0] , you are accessing a pair at the index of 0 returned by this function Object.entries(object) . The order of this array has nothing to do with how you defined the object3 in the first place. The order according to the doc is the same as the provided by a
for...in loop. I ran the for...in loop on the object and this is what i got as the order.
2,7,100.
This is why you are getting ["2", "b"] instead of ["100", "a"]. As others have mentioned here , the order seems to be that way because 2<7<100.
This is because the object has numeric keys and when you manipulate the object with the numeric keys the javascript sort the key-values in ascending order of the key value and since you have keys 2, 7, and 100. So, check this when you console.log the object Object.entries(object3) you get that sorted and when you access [0] you get Array ["2", "b"]
const object3 = { 100: 'a', 2: 'b', 7: 'c' };
console.log(Object.entries(object3));
Further clarification on sorting the object by ascending values of key when they are numeric. The javascript sorts them behind the scenes.
var a = {
10: 'ten',
5: 'five',
11: 'eleven',
1: 'one'
};
console.log(a);
This question already has answers here:
Why does spread syntax convert my string into an array?
(5 answers)
Closed 4 years ago.
Here,we created Hashmap with keys and value.Then we use array.map to get our result. i.e If we input "cat",we will get output as "dog".I didnt get how spread syntax is used.
var rule =
{
"c": "d",
"a": "o",
"t": "g",
"h": "a",
"e": "n",
"n": "t",
}
function output(str) {
return [...str].map(d => rule[d]).join('')
}
console.log(output('cat'))
Break it down:
[...str]
Gives: ['c', 'a', 't'];
In this case, the spread syntax breaks up the string into an array. Where each index represents a letter from the string.
.map(d => rule[d])
Converts the above array to:
['d', 'o', 'g']
As it goes through each value and uses the javascript object to get its value: (so 'c' maps to 'd' as rule['c'] = 'd', 'a' maps to 'o', etc...)
.join('')
Converts the above array to a string dog
Thus, doing:
console.log(output('cat'))
Gives: "dog"
spread syntax on a string turns it into an array consisting of its characters. This works because strings are iterable collections. http://es6-features.org/#SpreadOperator
In your case the spread operator spreads the string "cat" into an array of characters containing ['c','a','t']
Then it iterates over this array to provide the output string matches the values. There you get the string "dog" as output!
You would not be able to apply map to the string directly, as String does not have such a method. With the spread syntax you get an array of individual characters, and then map can be applied to that array.
It is in fact better practice to use Array.from instead, because it does not create the intermediate array and still allows you to execute a function on each character (using the second argument):
Array.from(str, d => rule[d]).join('')
It takes an iterable and returns each item of the iterable by using the spread syntax ....
In this case, a string is splitted to an array of characters.
['c', 'a', 't']
The same result could be achieved, if you use Array.from, which has a built-in mapping parameter.
var rule = { c: "d", a: "o", t: "g", h: "a", e: "n", n: "t" }
function output(str) {
return Array.from(str, d => rule[d]).join('');
}
console.log(output('cat')) ;