Best way to turn an array-like object into an array? - javascript

Suppose we have the following:
var o = {"1": 1, "2": 2, "5": 5};
And I wanted to turn it into what I would get if I did:
var o = []; o[1] = 1, o[2] = 2, o[5] = 5;
How could I do this?

Try this:
var o = { ... }; // your object
var oArr = [];
for (var i in o) {
if (parseInt(i) == i) {
oArr[parseInt(i)] = o[i];
}
}
Notice that it won't accept keys that are non numeric.

If you have a proper length property, it's really easy:
var o = {"1": 1, "2": 2, "5": 5, 'length' : 6};
o = Array.prototype.slice.call(o); // [undefined, 1, 2, undefined, undefined, 5]
If you don't have the length property, you can compute it:
var o = {"1": 1, "2": 2, "5": 5};
o.length = Object.keys(o).reduce(function(max,key){
return isNaN(key) ? max : Math.max(max, +key);
},-1) + 1;
o = Array.prototype.slice.call(o); // [undefined, 1, 2, undefined, undefined, 5]
One thing to note, though, when you access a property of an object, it is always converted to a string, so the following will work for your example, even if o is not an array:
var o = {"1": 1, "2": 2, "5": 5};
o[1] // 1
o[2] // 2
o[5] // 5
o[0] // undefined

You should not be doing that,
1. You are not sure about how big the maximum index value can be.
2. There could be lots of gaps, those indexes in the array will be null,
So, just use a string conversion of the index number and look it up in the same object.
var o = {"1": 1, "2": 2, "5": 5};
var index = 5;
o[index.toString()] // gives 5

Related

Chunk an array of objects and add empty arrays at the end

I need to chunk an array of objects so i write:
function conditionalChunk(array, size, rules = {}) {
let copy = [...array],
output = [],
i = 0;
while (copy.length)
output.push(copy.splice(0, rules[i++] ?? size))
return output
}
and it works fine if I have rules like { 0: 2, 1: 2 }
const input = [[1,2,3],[4,5,6,7]],
output = conditionalChunk(input.flat(), 3, { 0: 2, 1: 2 });
// OUTPUT: [[1,2],[3,4],[5,6,7]]
but when I have rules at the end like { 0: 2, 1: 2, 2:0, 5:0 } my function ignore to create empty arrays at the end.
the output I need is:
const input = [[1,2,3],[4,5,6,7]],
output = conditionalChunk(input.flat(), 3, { 0: 2, 1: 2, 2:0, 5:0 });
// OUTPUT: [[1,2],[3,4],[],[5,6,7],[]]
so I just need to not ignore rules for empty arrays at the end of array. How I can do that?
Finally, you could check the keys of rules and if greater or equal to the next index of output push an empty array.
function conditionalChunk(array, size, rules = {}) {
let output = [],
i = 0;
while (i < array.length)
output.push(array.slice(i, i += rules[output.length] ?? size));
let l = Object.keys(rules).reduce((l, v) => l + (v >= output.length), 0);
while (l--) output.push([]);
return output;
}
console.log(conditionalChunk([1, 2, 3, 4, 5, 6, 7], 3, { 0: 2, 1: 2 })); // [[1,2],[3,4],[5,6,7]]
console.log(conditionalChunk([1, 2, 3, 4, 5, 6, 7], 3, { 0: 2, 1: 2, 2: 0, 5: 0 })); // [[1,2],[3,4],[],[5,6,7],[]]
console.log(conditionalChunk([1, 2, 3, 4, 5, 6, 7], 3, { 0: 0, 1: 2, 8: 0, 7: 0, 9:0, 20:0 }));
.as-console-wrapper { max-height: 100% !important; top: 0; }
maybe you can try this, I prefer for...each loop in place of a while.
function conditionalChunk(array, size = 1, rules = {}) {
let copy = [...array],
output = [],
i = 0;
for (const rule of Object.keys(rules)) {
let val = copy.splice(0, rules[rule] || size);
if (typeof val === 'undefined') val = [];
output.push(val);
}
return output;
}
var results = conditionalChunk([1, 2, 3, 4, 5], 2, { 0:2, 1: 2, 2:2, 3:0 });
console.log(results, '::results::');
https://jsfiddle.net/5ozbj7na/
A solution to your problem would be to add this bit of code after the while loop before the return statement
output.length = Math.max.apply(null, [output.length, ...Object.keys(rules)]) || output.length;
for (r in rules)
{
output[r] = new Array(rules[r]);
}
It just extends the output array to the desired length then populates required spots with empty arrays (sorry for the complicated first line but JavaScript is complicated so :| ...)
Please try this one, this should work for you and is done based on your solution, so it should be easier to understand
function conditionalChunk(array, size, rules = {}) {
let copy = [...array],
output = [],
i = 0;
while (copy.length) output.push(copy.splice(0, rules[i++] ?? size));
const arr = Object.keys(rules).filter((key) => key > i);
arr.forEach((key) => {
if (rules[key] === 0) output.push([]);
});
return output;
}

Check if array has the key value pairs of the other array

I have two sets of arrays:
var a = [1, 2, {a: 1, b:2}, 3];
var b = [1, {a: 1, b: 2}, 3};
If array a has ALL (EXACT) the properties and values of array b (including objects) then it will increment count by 1. Otherwise, if array a has only some properties and some values doesn't match, it will just exit and do nothing.
As an example:
var a = [1, 2, {a: 1, b:2}, 3];
var b = [1, {a: 1, b: 2}, 3};
// return true / count++
var a = [1, 2, 3, {a: 1, b: 4}];
var b = [1, 2, {a: 1, b: 6}, 3];
return false / no count
Here's my program first:
for(var i = 0; i < mainArr[i]; i++){
if(arr.includes(mainArr[i])){
count++;
}
if(typeof(mainArr[i]) === 'object' && typeof(arr[i]) === 'object'){
for(var mainArrProp in mainArr[i]){
for(var arrProp in arr[i]){
if(arr[i].hasOwnProperty(mainArrProp) && arr[i][arrProp] === mainArr[i][mainArrProp]){
count++;
}
}
}
}
}
Any way to fix this?
If you can use es6, you can leverage some of the new methods to make a (relatively) short function to do this. Specifically we would make use of the array includes, every, and some methods to check for shallow object equality:
function getIncrementValue(set, subset) {
for (let item of subset) {
if (typeof item === "object") {
// Special case if looking at an object. I am assuming object is always shallow.
if (!set.some(otherItem => typeof otherItem === "object" && Object.keys(otherItem).length === Object.keys(item).length && Object.keys(otherItem).every(key => (key in item) && item[key] === otherItem[key]))) return 0;
} else {
if (!set.includes(item)) return 0;
}
}
return 1;
}
// So for your arrays:
var a1 = [1, 2, {a: 1, b:2}, 3];
var b1 = [1, {a: 1, b: 2}, 3];
// return true / count++
var a2 = [1, 2, 3, {a: 1, b: 4}];
var b2 = [1, 2, {a: 1, b: 6}, 3];
// return false / no count
console.log(getIncrementValue(a1, b1)) // returns 1;
console.log(getIncrementValue(a2, b2)) // returns 0;
This is pretty straightforward except for that one long line that checks for object equality. But all that says is:
If there is not some element of set that:
is an object, and
the element and our current item have the same number of keys, and
every key of that element is a key of our current item, and
the values for each key are the same
Then return 1.
You're following totally wrong way, you don't need to work with regular count++ in your case, you should better decrease by one and return, if any value from arrayA doesn't exist in arrayB. I rewrote the method with explanation of each row, I think, it should be clear what's going on. I also noticed, that you write in ES5, but you also use includes(), it's a ES6 feature, good to know. I have left includes() in my implementation too. My method returns 1 if all values from the first array exists in the second, and returns 0 if not. Here it is:
function test(a, b) {
var count = 1,
/* "aValues" will contain both single numbers and object values from "a" array */
aValues = createHomogeneousArray(a),
/* "bValues" will contain both single numbers and object values from "b" array */
bValues = createHomogeneousArray(b);
/* Check does each value of "aValues" exists in "bValues" array, if not - return "count--" (will be equal to "0") */
aValues.forEach(function(item) {
if (!bValues.includes(item)) {
return count--
}
});
/* If nothing was returned yet, return count (is equal to "1") */
return count;
}
function createHomogeneousArray(arr) {
var finalArr = [];
/* Iterate through array */
arr.forEach(function(item) {
/* Check is object */
if (typeof item === 'object') {
/* If object - get all values of this object as array */
var arrOfObjValues = Object.values(item);
/* Iterate through this array with object values */
arrOfObjValues.forEach(function(value) {
/* Push each value of above array to the "finalArr" array, which contains ALL values of the array, passed to this function */
finalArr.push(value);
});
/* Check is number */
} else if (typeof item === 'number') {
/* Push all regular numbers to final array */
finalArr.push(item);
}
});
/* Return "finalArr" array */
return finalArr;
}
console.log(test(
[1, 2, {a: 1, b:2}, 3],
[1, {a: 1, b: 2}, 3]
))
console.log(test(
[1, 2, 3, {a: 1, b: 4}],
[1, 2, {a: 1, b: 6}, 3]
))
Don't be scared of the size of the code, it's much smaller without comments:
function test(a, b) {
var count = 1,
aValues = createHomogeneousArray(a),
bValues = createHomogeneousArray(b);
aValues.forEach(function(item) {
if (!bValues.includes(item)) {
return count--
}
});
return count;
}
function createHomogeneousArray(arr) {
var finalArr = [];
arr.forEach(function(item) {
if (typeof item === 'object') {
var arrOfObjValues = Object.values(item);
arrOfObjValues.forEach(function(value) {
finalArr.push(value);
});
} else if (typeof item === 'number') {
finalArr.push(item);
}
});
return finalArr;
}
console.log(test(
[1, 2, {a: 1, b:2}, 3],
[1, {a: 1, b: 2}, 3]
))
console.log(test(
[1, 2, 3, {a: 1, b: 4}],
[1, 2, {a: 1, b: 6}, 3]
))

Explanation for in loop JavaScript

Who can explain how this for in loop works and why it's assign keys of object to array
var o = {
a: 1,
b: 2,
c: 3,
d: 4
};
var a = [],
i = 0;
for (a[i++] in o);
console.log(a);
Using a side effect when enumerating the object, and using an empty statement, each key is stored in the array a; first key in a[0], next in a[1] etc.
It is not necessary however since you could just use Object.keys(o)
var o = {
a: 1,
b: 2,
c: 3,
d: 4
};
var a = [],
i = 0;
for (a[i++] in o); // do nothing loop
console.log(a,Object.keys(o));

Jumble numbers in an array such that no two adjacent numbers are same using JavaScript

The idea it to basically not have repeated values in the array with similar values.
An example input array:
input = [1,2,2,2,2,3,4,5,6,7,8,9]
Expected output to be something like this:
desiredOutput = [1,2,3,2,4,2,5,2,6,2,7,8,9]
I have tried putting this in a for loop where it checks with the next item and if it is same, swaps the values. The problem is when I have continuous similar values.
This proposal features
count of elements and store it in an appropriate object,
check whether spread is possible (e.g. not here [1, 1, 1, 1, 3, 3]),
round robin with the elements, so
maximum distance between the same elements.
How does it work?
As example I take this array: [1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9]
Build an object with the count of the elements, store it with the element as key.
length = {
"1": 1, "2": 4, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1, "9": 1
}
Select the property with the largest value: length[2] = 4
Make a new array with the length of the previous value and fill it with empty arrays.
output = [[], [], [], [], []]
Check if a spreaded array is possible. If not, return.
Set k to the key of the biggest value of a property.
k = '2'
If truthy, proceed. Otherwise go to 11.
Set l to the value of length[k].
l = 4
Iterate over l and push k to the end of the array with the index of i % outputLength. Increase i.
Delete property k.
Proceed with 5.
Return the flat output array.
output first then continued
array 0: 2 1 6
array 1: 2 3 7
array 2: 2 4 8
array 3: 2 5 9
return: 2 1 6 2 3 7 2 4 8 2 5 9
distance | | | | is equal
function spread(input) {
function findMaxKey() {
var max = 0, key;
Object.keys(length).forEach(function (k) {
if (length[k] > max) {
max = length[k];
key = k;
}
});
return key;
}
var length = input.reduce(function (r, a) {
r[a] = (r[a] || 0) + 1;
return r;
}, {}),
i = 0, k = findMaxKey(), l,
outputLength = length[k],
output = Array.apply(Array, { length: outputLength }).map(function () { return []; });
if (input.length - outputLength < outputLength - 1 ) {
return; // no spread possible
}
while (k = findMaxKey()) {
l = length[k];
while (l--) {
output[i % outputLength].push(k);
i++;
}
delete length[k];
}
return output.reduce(function (r, a) { return r.concat(a) }, []);
}
console.log(spread([1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9]));
console.log(spread([1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2]));
console.log(spread([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]));
console.log(spread([1, 1, 1, 1, 3, 3]));
console.log(spread([1, 1, 3]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
maybe that could help you:
for(var i = 1; i < input.length; i++) {
if(input[i-1] == input[i]) {
var j = i;
while(j < input.length && input[j] == input[i]) {
j++;
}
var el = input[j];
input[j] = input[i];
input[i] = el;
}
}
Greedy Approach Using Max Heap
The idea is to greedily put the highest frequency numbers first.
Construct a max heap where every node is a tuple that stores the number & it's frequency.
Then extract the head of the max heap (the highest frequency node) and add it's value to the resultant array.
If there's a previous element then add it back to the heap.
Decrement the frequency of the extracted node and store it in prev, so that it can be added back after one iteration.
Finally return the solution if it exists otherwise return the string "Not Possible".
function solution(arr) {
const maxHeap = Array.from(
arr.reduce((m, i) => m.set(i, (m.get(i) ?? 0) + 1), new Map())
).sort(([, a], [, b]) => b - a);
const res = [];
let prev = null;
while (maxHeap.length) {
const maxNode = maxHeap.shift();
res.push(maxNode[0]);
maxNode[1] -= 1;
if (prev) {
maxHeap.push(prev);
maxHeap.sort(([, a], [, b]) => b - a);
prev = null;
}
if (maxNode[1] > 0) {
prev = maxNode;
}
}
return res.length < arr.length ? "Not Possible" : res;
}
console.log(JSON.stringify(solution([1, 2, 2, 2, 2, 3, 4, 5, 6, 7, 8, 9])));
console.log(JSON.stringify(solution([1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2])));
console.log(JSON.stringify(solution([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3])));
console.log(JSON.stringify(solution([1, 1, 1, 1, 3, 3])));
console.log(JSON.stringify(solution([1, 1, 3])));
Note: I've not implemented a Max Heap (because it's tedious), I've simulated it with Array.prototype.sort.

Getting key with the highest value from object

I have a object like that one:
Object {a: 1, b: 2, undefined: 1}
How can I quickly pull the largest value identifier (here: b) from it? I tried converting it to array and then sorting, but it didn't work out, since it got sorted alphabetically (and it seems like a overkill to juggle data back and forth just for getting one value out of three).
For example:
var obj = {a: 1, b: 2, undefined: 1};
Object.keys(obj).reduce(function(a, b){ return obj[a] > obj[b] ? a : b });
In ES6:
var obj = {a: 1, b: 2, undefined: 1};
Object.keys(obj).reduce((a, b) => obj[a] > obj[b] ? a : b);
Using Underscore or Lo-Dash:
var maxKey = _.max(Object.keys(obj), function (o) { return obj[o]; });
With ES6 Arrow Functions:
var maxKey = _.max(Object.keys(obj), o => obj[o]);
jsFiddle demo
Here is a suggestion in case you have many equal values and not only one maximum:
const getMax = object => {
return Object.keys(object).filter(x => {
return object[x] == Math.max.apply(null,
Object.values(object));
});
};
This returns an array, with the keys for all of them with the maximum value, in case there are some that have equal values.
For example: if
const obj = {apples: 1, bananas: 1, pears: 1 }
//This will return ['apples', 'bananas', 'pears']
If on the other hand there is a maximum:
const obj = {apples: 1, bananas: 2, pears: 1 }; //This will return ['bananas']
---> To get the string out of the array: ['bananas'][0] //returns 'bananas'`
Supposing you've an Object like this:
var obj = {a: 1, b: 2, undefined: 1}
You can do this
var max = Math.max.apply(null,Object.keys(obj).map(function(x){ return obj[x] }));
console.log(Object.keys(obj).filter(function(x){ return obj[x] == max; })[0]);
{a: 1, b: 2, undefined: 1}
The best work around I've seen is this
const chars = {a: 1, b: 2, undefined: 1}
//set maximum value to 0 and maxKey to an empty string
let max = 0;
let maxKey = "";
for(let char in chars){
if(chars[char]> max){
max = chars[char];
maxKey= char
}
}
console.log(maxKey)
Very basic method. might be slow to process
var v = {a: 1, b: 2, undefined: 1};
function geth(o){
var vals = [];
for(var i in o){
vals.push(o[i]);
}
var max = Math.max.apply(null, vals);
for(var i in o){
if(o[i] == max){
return i;
}
}
}
console.log(geth(v));
Combination of some ideas from other answers. This will get all the keys with the highest value, but using the spread operator to get the maximum value and then filter array method:
const getMax = object => {
let max = Math.max(...Object.values(object))
return Object.keys(object).filter(key => object[key]==max)
}
let obj = {a: 12, b: 11, c: 12};
getMax(obj)
let data = {a:1,b:2,undefined:3}
let maxValue = Object.entries(data).sort((x,y)=>y[1]-x[1])[0]
note: this is a very expensive process and would block the event loop if used with objects of large sizes(>=1000000). with large array slice the entries and call the above method recurrently using setTimeout.
If you need to return an array from an object with the keys for all duplicate properties with the (same or equal) highest value, try this:
const getMax = Object.keys(object)
.filter(x => {
return object[x] == Math.max.apply(null,
Object.values(object));
})
var object = { orange: 3, blue: 3, green: 1}
console.log(getMax) // ['orange', 'blue']

Categories