Related
Suppose I have an object {'a':20,'b':6,'c':21,'d':12}
After getting sort by values in descending order,
The desired output : Keys: ['c','a','d','b'] and Values: [21,20,12,6]
How can I achieve that efficiently? I came to knew that we cannot we cant sort js object keys based on values, since the order of keys is random everytime.
I came to knew that we cannot we cant sort js object keys based on values, since the order of keys is random everytime.
That's not true (it's not random, and it's not different every time), but sorting JavaScript objects is almost never useful.
You have several options to get the array you've shown. One simple one is to use Object.entries to get an array of [key, value] arrays, then sort that, then get just the keys from it via map:
const obj = {'a':20,'b':6,'c':21,'d':12};
const keys = Object.entries(obj)
.sort(([k1, v1], [k2, v2]) => v2 - v1)
.map(([k, v]) => k);
console.log(keys);
Object.entries is relatively new, but easily polyfilled if needed.
If you need it in ES5 (with Object.entries polyfilled):
var obj = {'a':20,'b':6,'c':21,'d':12};
var keys = Object.entries(obj)
.sort(function(e1, e2) {
return e2[1] - e1[1];
})
.map(function(e) {
return e[0];
});
console.log(keys);
In a comment you've asked how to have the values in a separate array as well. You can either build that value array as a side-effect of map by pushing into it, like this:
const obj = {'a':20,'b':6,'c':21,'d':12};
const values = [];
const keys = Object.entries(obj)
.sort(([k1, v1], [k2, v2]) => v2 - v1)
.map(([k, v]) => {
values.push(v);
return k;
});
console.log(keys);
console.log(values);
...or if using map for side-effects bothers you (it's often not best practice), just use forEach (or for-of) instead:
const obj = {'a':20,'b':6,'c':21,'d':12};
const keys = [];
const values = [];
Object.entries(obj)
.sort(([k1, v1], [k2, v2]) => v2 - v1)
.forEach(([k, v]) => {
keys.push(k);
values.push(v);
});
console.log(keys);
console.log(values);
The for-of version:
const obj = {'a':20,'b':6,'c':21,'d':12};
const keys = [];
const values = [];
for (const [k, v] of Object.entries(obj).sort(([k1, v1], [k2, v2]) => v2 - v1)) {
keys.push(k);
values.push(v);
}
console.log(keys);
console.log(values);
(You might use a temporary variable for the result of .sort, then use for (const [k, v] of theTemporaryVariable) {.)
const obj = {'a':20,'b':6,'c':21,'d':12};
const result = Object.keys(obj)
.reduce((acc, key) => {
const value = obj[key];
acc.push({ key, value });
acc.sort((a, b) => b.value - a.value);
return acc;
}, []);
console.log(result);
output
[
{ key: 'c', value: 21 },
{ key: 'a', value: 20 }
{ key: 'd', value: 12 }
{ key: 'b', value: 6 }
]
Try this.
const list = {'a':20,'b':6,'c':21,'d':12};
let myObject = Object.fromEntries(Object.entries(list).sort( (a,b) => b[1] - a[1] ));
Object.prototype.mySplit = function() {
return Object.keys(this).map((key) => {
return {
key: key,
value: this[key]
};
});
}
let keys = [];
let values = [];
let properties = myObject.mySplit();
for (var i = 0; i < properties.length; i++) {
keys.push(properties[i]['key']);
values.push(properties[i]['value']);
}
console.log(keys);
console.log(values);
1) Get keys from an object
2) Sort keys based on val comparision
function cmp(a, b) {
return (a < b) ? 1 : (a > b) ? -1 : 0
}
function sortObj(obj) {
const keys = Object.keys(obj).sort((x,y) => cmp(obj[x],obj[y]))
const vals = keys.map(x => obj[x])
return {sortedKeys: keys, sortedValues: vals}
}
result = sortObj({'a':20,'b':6,'c':21,'d':12})
console.log(result)
Answer
{
sortedKeys: ["c", "a", "d", "b"]
sortedValues: [21, 20, 12, 6]
}
Problem
I have a string of numerical values separated by commas, and I want to include them in an array, and also each pair of them to be an array nested inside of the main array to be my drawing vertices.
How do I solve this problem?
Input:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
what I want them to be is:
Output:
var V_array = [[24,13],[47,20],[33,9],[68,18],[99,14],[150,33],[33,33],[34,15],[91,10]];
You could Split on every second comma in javascript and map the splitted pairs by converting the values to number.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
result = vertices.match(/[^,]+,[^,]+/g).map(s => s.split(',').map(Number));
console.log(result);
You can use the function reduce which operates over the splitted-string and check for the mod of each index.
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = str.split(',').reduce((a, s, i) => {
a.curr.push(Number(s));
if ((i + 1) % 2 === 0) {
a.arr.push(a.curr);
a.curr = [];
}
return a;
}, {arr: [], curr: []}).arr;
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
You can split string into array and use reduce method. Take a look at the code below
const vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const numbers = vertices.split(',').map(Number)
const res = numbers
.reduce((acc, number, index, srcArray) => {
if (index % 2) {
return acc
}
return [
...acc,
[ number, srcArray[index + 1] ],
]
}, [])
console.log(res)
My two cents :) [new version]
let
str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10",
pair = [],
triplet = [];
JSON.parse(`[${str}]`).forEach((e,i)=>{pair.push( (i%2)?[pair.pop(),e]:e)})
console.log ( 'pair:', JSON.stringify(pair) )
// bonus => same idea for triplet :
JSON.parse(`[${str}]`).forEach((e,i)=>{
if ( (i%3)===2 ) triplet.push( [triplet.shift(),triplet.pop(),e] )
else if ( (i%3)===0 ) triplet.unshift(e)
else triplet.push(e)
})
console.log ( 'triplet:', JSON.stringify(triplet) )
You can use exec and JSON.parse
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
var array1;
var reg = /[^,]+,[^,]+/g
let op = []
while((array1 = reg.exec(vertices))!== null){
op.push(JSON.parse(`[${array1[0]}]`))
}
console.log(op)
Split on the , and use Array.reduce to group the pair into a new 2-D array:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").reduce((acc, ele, idx, arr) => {
if(idx === 0 || idx%2 === 0) {acc.push([+ele, +arr[idx + 1]]);}
return acc;
}, []);
console.log(pair);
Same can be done using Array.map, if the index is odd skip the element and filter out the undefined elements:
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
const pair = vertices.split(",").map((ele, idx, arr) => (idx === 0 || idx%2 === 0) ? [+ele, +arr[idx + 1]] : undefined).filter(e => e);
console.log(pair);
My two cents :)
( thanks to Code Maniac for the idea of using JSON.parse )
let str = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
let result = JSON.parse(`[${str}]`).reduce((acc, cur, i) => {
if (i===1) return [[acc,cur]]
if (i%2) acc.push( [acc.pop(), cur] )
else acc.push( cur )
return acc
});
console.log ( result )
Here is my solution.
var vertices = "24,13,47,20,33,9,68,18,99,14,150,33,33,33,34,15,91,10";
vertices = vertices.split(",");
function convertToMultiArray (arr, length) {
var nArr = [];
while(arr.length > 0) {
nArr.push(arr.splice(0,length));
}
return nArr;
}
const res = convertToMultiArray(vertices, 2);
console.log('res', res);
I would like to create a JS function that transform a generic collection (an array or object of nested levels of arrays or objects) into an object that his keys are an input array argument (if provided, if not all distinct objects keys in input) and values are an array of leaf values for these keys in input). Leaf values can be distinct if flag provided.
Something like:
transform([{a:1, b:'1', c:true, d:1},{a:'1', b:2, c:3, d:false}, {a:1, c:'test'}], ['a','b','c'], true);
OR
transform({x:{b:2,c:{c:3, d:1}},b:'1',z:{b:2,c:true,a:1},a:'1',g:{c:'test',d:false}}, ['a','b','c'], true);
Will produce the same output:
{a:[1, '1'], b:['1', 2], c:[true, 3, 'test']}
First argument is the collection - mandatory
Second one is array of keys - optional
Third argument is the unique flag - optional
If second argument is omitted. It will produce this :
{a:[1, '1'], b:['1', 2], c:[true, 3, 'test'], d:[1,false]}
What is the fast and/or elegant way to do it?
Is there any lodash/underscore helper for it.
Thanks.
The basics
The main part of this problem is walking the object trees recursively and collecting the values.
To do so, we write a function that looks at a value and determines if it's an object. If it isn't, we concatenate the value to a results array. If it is, we reduce its values using the same logic.
const deepValues = (obj, res = []) =>
isObject(obj)
? Object.values(obj).reduce(flip(deepValues), res)
: res.concat(obj)
console.log(
deepValues({x:{b:2,c:{c:3, d:1}}})
);
// Utils
function flip(f) { return (x, y) => f(y, x) };
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
Customizing it
Once you understand this function, adding the other bits and pieces makes a bit more sense.
Step 1: Not all the values...
So we want to concatenate the value to the results if its key matches our criterium.
We add a keyPred function to our signature and start working with [key, value] pairs rather than just the values. The predicate takes a key and returns a boolean that indicates if we should store it.
The function becomes a bit harder to read, but it's not that big of a difference from the initial one:
const certainDeepValues = (keyPred, obj, res = []) =>
Object.entries(obj)
.reduce((r, [k, v]) =>
isObject(v)
? certainDeepValues(keyPred, v, r)
: keyPred(k) ? r.concat(v) : r
, res);
console.log(
certainDeepValues(
k => k === "c",
{x:{b:2,c:{c:3, d:1}}}
)
);
// Utils
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
Step 2: Only collect unique values
Javascript has the perfect type for a collection of unique values: a Set, which makes this a small change. The default argument is new Set and the concat method becomes add.
const certainDeepValues = (keyPred, obj, res = new Set()) =>
Object.entries(obj)
.reduce((r, [k, v]) =>
isObject(v)
? certainDeepValues(keyPred, v, r)
: keyPred(k) ? r.add(v) : r
, res);
console.log(
[...certainDeepValues(
k => k === "c",
{x:{b:2,c:{c:3, d:1, e: { c: "2" }}}, c: 3}
)]
);
// Utils
function flip(f) { return (x, y) => f(y, x) };
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
Your own signature
Now, all there's left is to implement the desired signature. This is where it gets a bit ugly, but since you didn't post any attempts and merely wrote your "question" as an assignment to us, I'll leave it up to you to clean it up :)
const certainDeepValues =
(concat, keyPred, obj, res = new Set()) =>
Object.entries(obj)
.reduce((r, [k, v]) =>
isObject(v)
? certainDeepValues(concat, keyPred, v, r)
: keyPred(k) ? concat(r, k, v) : r
, res);
const transform = (xs = [], keys = [], uniquesOnly = false) => {
const keySet = new Set(keys);
const keyPred = keys.length
? k => keySet.has(k)
: k => true;
const concat = uniquesOnly
? (acc, k, v) => (acc[k] = (acc[k] || new Set()).add(v), acc)
: (acc, k, v) => (acc[k] = (acc[k] || []).concat(v), acc)
const unwrap = uniquesOnly
? acc => mapObj(s => [...s], acc)
: acc => acc;
return unwrap(xs.reduce(
(r, x) => certainDeepValues(concat, keyPred, x, r),
{}
));
};
console.log(
JSON.stringify(
transform([{a:1, b:'1', c:true, d:1},{a:'1', b:2, c:3, d:false}, {a:1, c:'test'}], ['a','b','c'], true)
)
);
console.log(
JSON.stringify(
transform([{x:{b:2,c:{c:3, d:1}},b:'1',z:{b:2,c:true,a:1},a:'1',g:{c:'test',d:false}}], ['a','b','c'], true)
)
);
console.log(
JSON.stringify(
transform([{x:{b:2,c:{c:3, d:1}},b:'1',z:{b:2,c:true,a:1},a:'1',g:{c:'test',d:false}}], undefined, true)
)
);
// Utils
function mapObj(f, obj) { return Object.assign({}, ...Object.entries(obj).map(([k, v]) => ({[k]: f(v)}))); }
function flip(f) { return (x, y) => f(y, x) };
function isObject(x) { return Object.prototype.toString.call(x) === "[object Object]" };
I am having 2 element list which is like this,
a = ['first_name', 'user']
b = ['last_name', 'abcd']
I want to convert it into,
{
'first_name' : 'user',
'last_name' : 'abcd'
}
This is what I tried:
a = ['first_name', 'user']
b = ['last_name', 'abcd']
new_dict = {}
new_dict[a[0]] = a[1]
new_dict[b[0]] = b[1]
console.log(new_dict)
Are there any simple/native JS method than this?
You could place the a and b arrays in an outer array, and then reduce that array into a single object.
var o = [a,b].reduce(function(p, c) { p[c[0]] = c[1]; return p; }, {});
You can use Object.assign with map() and spread syntax.
var a = ['first_name', 'user'], b = ['last_name', 'abcd']
var obj = Object.assign({}, ...[a, b].map(([k, v]) => ({[k]: v})))
console.log(obj)
You could use Array#reduce with Object.assign and computed property names with a previous destructuring assignment.
var a = ['first_name', 'user'],
b = ['last_name', 'abcd'],
object = [a, b].reduce((r, [k, v]) => Object.assign(r, { [k]: v }), {});
console.log(object);
I have an array of strings:
array = ["Henry","Brian","Henry","Matilda","Henry","Brian","Matthew"]
And want to sort them into a list which orders the array by the most commonly occuring items first, but then also deletes them afterwards to create a list like so:
sortedArray = ["Henry","Brian","Matilda","Matthew"]
Is there a way of doing this in javascript?
You could use this ES6 function which runs in O(nlogn), not O(n²) as some other solutions:
var array = ["Henry","Brian","Henry","Matilda","Henry","Brian","Matthew"]
var result = [...array.reduce( (acc, s) => acc.set(s, (acc.get(s) || 0)+1), new Map )]
.sort( (a, b) => b[1] - a[1] )
.map( a => a[0] );
console.log(result);
It first creates a map, by keeping a count for each string (runs in linear time).
Then this map is transformed to an array of pairs (with spread [... ]), which is then sorted ( O(nlogn) ) by that count.
Finally, the count is dropped again from that array, using .map()
First create hash table with the array elements and the number of occurences - then sort it.
See demo below:
var array = ["Henry","Brian","Henry","Matilda","Henry","Brian","Matthew"];
var hash = array.reduce(function(p,c) {
p[c] = (p[c] || 0) + 1;
return p;
},{});
// console.log(hash);
var result = Object.keys(hash).sort(function(a,b){
return hash[b] - hash[a];
});
console.log(result);
array.sort((a, b) =>
array.filter(e => e === b).length - array.filter(e=> e === a).length
)
Then , remove duplicated items :
[... new Set(array)]
let array = ["Henry", "Brian", "Henry", "Matilda", "Henry", "Brian", "Matthew"]
array = array.sort((a, b) =>
array.filter(e => e === b).length - array.filter(e => e === a).length
)
console.log(
[...new Set(array)]
)
You could count all items and sort the keys later with the count descending.
var array = ["Henry", "Brian", "Henry", "Matilda", "Henry", "Brian", "Matthew"],
count = Object.create(null),
result;
array.forEach(function (a) {
count[a] = (count[a] || 0) + 1;
});
result = Object.keys(count).sort(function (a, b) { return count[b] - count[a]; });
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
I guess the hash method should be fastest, yet here is another way which utilizes .findIndex() to run only among the unique items which might turn out to be not so bad as well. It just swaps the unique items array (p) with the previous one if it's length is bigger than the previous one.
var data = ["Henry","Brian","Henry","Matilda","Henry","Brian","Matthew","Matthew","John","Matthew"],
result = data.reduce(function(p,c){
var fi = p.findIndex(f => f[0] === c);
return fi === -1 ? (p.push([c]), p)
: (p[fi].push(c),
fi ? (p[fi].length > p[fi-1].length && ([p[fi-1],p[fi]] = [p[fi],p[fi-1]]),p)
: p);
},[])
.map(e => e[0]);
console.log(result);