Javascript - Function on Array confusion - javascript

function arrayToList(array) {
var list = null
for (var i = 0; i < array.length; i++)
list = {value: array[i], rest: list};
return list;
}
console.log(arrayToList([10, 20, 30]));
// {value: 30, rest:{ value:20, rest:{value: 10, rest: null}}
I've looked at this function from eloquent javascript exercise for hours, and can't get it why the result are reversed. Any help would be appreciated.

Here's what's happening when you are iterating in your for-loop:
First iteration:
i = 0;
list = {value:10, rest:null}
Second iteration:
i = 1;
list = {value:20, rest:{value:10, rest: null}}
Third iteration:
i = 2;
list = {value:30, rest:{value:20, rest:{value:10, rest: null}}}
On each iteration, you are nesting your list object within itself:
list = {value: array[i], rest: list};

Set an output in the loop, than you can see how it works:
function arrayToList(array) {
var list = null
for(var i = 0; i < array.length; i++) {
list = {value: array[i], rest: list};
console.log(list);
console.log('\n');
}
return list;
}
arrayToList([10, 20, 30]);
/* Output
{ value: 10, rest: null }
{ value: 20, rest: { value: 10, rest: null } }
{ value: 30, rest: { value: 20, rest: { value: 10, rest: null } } }
*/
You have set list = null.
The first time in the loop, list is "{ value: 10, rest: null }"
-> "null" nested within.
The second time in the loop, list is "{ value: 20, rest: { value: 10, rest: null } }"
-> "{ value: 10, rest: null }" nested within.
For the last time, list is "{ value: 30, rest: { value: 20, rest: { value: 10, rest: null } } }"
-> "{ value: 20, rest: { value: 10, rest: null } }" nested within.

Related

Convert list object into array

I have a list object
{
value: 5,
rest: {
value: 10,
rest: {
value: 15,
rest: null
}
}
}
that should be converted into array. I was trying to iterate through the list to take the values and push them into array.
function listToArr(obj){
let arr = []
for (let val in object){
arr.push(Object.values(val))
}
return arr
}
But I am getting [ [ 'v', 'a', 'l', 'u', 'e' ], [ 'r', 'e', 's', 't' ] ]
You'd need to reassign the object inside a loop and access its value property:
console.log(listToArr({ value: 5, rest: { value: 10, rest: { value: 15, rest: null } } }));
function listToArr(obj){
const arr = [];
while (obj.rest) {
arr.push(obj.value);
obj = obj.rest;
}
arr.push(obj.value);
return arr;
}
Since the keys are static, using Object.values or for..in doesn't accomplish anything.
An ES6 style approach:
let listToArr = (obj) => obj.rest ? [obj.value, ...listToArr(obj.rest)] : [obj.value];
console.log(listToArr({ value: 5, rest: { value: 10, rest: { value: 15, rest: null } } }));
There's a nice recursive solution for this as well. Something like:
let a = {
value: 5,
rest: {
value: 10,
rest: {
value: 15,
rest: null
}
}
}
function listToArr(obj, arr){
arr = arr || [];
if (!obj) return arr;
return listToArr(obj.rest, arr.concat(obj.value));
}
console.log(listToArr(a));

Find the minimum and maximum values in the nested object

I have a deeply nested javascript object with an unlimited amout of children. Every child has a value.
var object = {
value: 1,
children: {
value: 10,
children:{
value: 2,
children: {...}
}
}
}
All attempts to make a recursive function did not succeed, it turned out to go down only to a lower level.
After flattening your linked list into an array, you can use Array.prototype.reduce() with an accumulator that is a tuple of min and max, starting with initial values of Infinity and -Infinity respectively to match the implementations of Math.min() and Math.max():
const object = {
value: 1,
children: {
value: 10,
children: {
value: 2,
children: {
value: 5,
children: null
}
}
}
}
const flat = o => o == null || o.value == null ? [] : [o.value, ...flat(o.children)]
const [min, max] = flat(object).reduce(
([min, max], value) => [Math.min(min, value), Math.max(max, value)],
[Infinity, -Infinity]
)
console.log(min, max)
Since children is an object with only one value (vs an array with potentially many), this is a pretty simple recursive function. The base case is when there are no children in which case both min and max are just the value. Otherwise recurse on the children to find the min and max:
var object = {
value: -10,
children: {
value: 4,
children:{
value: 200,
children: {
value: -100,
children: null
}
}
}
}
function getMinMax(obj) {
if (!obj.children || obj.children.value == undefined)
return {min: obj.value, max: obj.value}
else {
let m = getMinMax(obj.children)
return {min: Math.min(obj.value, m.min), max: Math.max(obj.value, m.max)}
}
}
console.log(getMinMax(object))
Short and simple, for min change Math.max to Math.min
var test = {
value: 1,
children: {
value: 10,
children:{
value: 2,
children: {}
}
}
}
function findMaxValue(obj) {
if (Object.keys(obj.children).length === 0) {
return obj.value;
}
return Math.max(obj.value, findMaxValue(obj.children))
}
console.log(findMaxValue(test))

Reduce complexity O^2 of comparison between list of objects

I have two list of objects:
list1 = [{value: 'X'}, {value: 'Y'}, ..., {value: 'Z'}];
list2 = [{value: 'A'}, {value: 'B'}, ..., {value: 'C'}];
I have this code that checks whether the values in list2 are in list1. If it is the code doesn't do anything, if not it should add to list1 (this will create a new list, list3). Which means I'm doing a union between the two list without keeping the repeated values.
for (let i = list2.length-1; i >= 0; i--) {
let item = list2[i];
let shared = false;
for (let j = list1.length-1; j >=0; j--) {
let childItem = list1[j];
if (item.value === childItem.value) {
shared = true;
break;
}
}
if (!shared) { newValues.push(item); }
}
list3 = list1.concat(newValues);
This works fine, but I was wondering if I could improve this O(n*m).
I'm not sure if the lists are always sorted by default, but from what I've seen both (list1 and list2) are always sorted by value.
Example:
var list1 = [{value: 'bar'}, {value: 'baz'}, {value: 'foo'}, {value: 'foz'}];
var list2 = [{value: 'bar'}, {value: 'foo'}, {value: 'test'}, {value: 'testz'}];
var list3 = union(list1, list2);
list3 = [{value: 'bar'}, {value: 'baz'}, {value: 'foo'}, {value: 'foz'}, {value: 'test'}, {value: 'testz'}];
Create a set of the values of list1, and Filter list2 by the values in the set before concating it to list1:
var list1 = [{value: 'bar'}, {value: 'baz'}, {value: 'foo'}, {value: 'foz'}];
var list2 = [{value: 'bar'}, {value: 'foo'}, {value: 'test'}, {value: 'testz'}];
const union = (list1, list2) => list1.concat(
list2.filter(function({ value }) { // filter list2
return !this.has(value); // filter out items which value is in the set
}, new Set(list1.map(({ value }) => value))) // the set of list1 values
);
const list3 = union(list1, list2);
console.log(list3);
You could use a single loop and store the value in a set for later check. Complexity: O(n).
var list1 = [{ value: 'X' }, { value: 'Y' }, { value: 'C' }, { value: 'Z' }],
list2 = [{ value: 'A' }, { value: 'B' }, { value: 'C' }, { value: 'D' }],
list3 = list1
.concat(list2)
.filter((s => ({ value }) => !s.has(value) && s.add(value))(new Set));
console.log(list3);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Convert a list into an array

I have a function that takes as an argument a list and must return the elements of the list into an array.
For example in case of the input:
{value: 1, rest: {value: 2, rest: null}}
The output should looke like:
[1, 2]
This is how I tried to solve it:
function listToArray(list){
var arr = [];
for (var node = list; node; node = node.rest) {
arr.unshift(node);
}
return arr;
}
console.log(listToArray({value: 1, rest: {value: 2, rest: null}}));
And the output I get is:
[{value: 2, rest: null}, {
value: 1
rest: {value: 2, rest: null}
}]
Does anyone know what should I change to make it work?
You were just missing the .value from node.
function listToArray(list){
var arr = [];
for (var node = list; node; node = node.rest) {
arr.unshift(node.value);
}
return arr;
}
console.log(listToArray({value: 1, rest: {value: 2, rest: null}}));
Note you might want push instead of unshift.
You can use recursion to grab all the inner object values.
var obj = {value: 1, rest: {value: 2, rest: null}};
var list = objToList(obj, 'value', 'rest');
console.log(list);
function objToList(obj, valueField, targetField) {
return objToListInner(obj, valueField, targetField, []);
}
function objToListInner(obj, valueField, targetField, list) {
if (isObject(obj)) {
list.push(obj[valueField]);
objToListInner(obj[targetField], valueField, targetField, list)
}
return list;
}
function isObject(obj) {
return obj !== null && typeof obj === 'object';
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}
A bit of code golf. ;)
let obj = {value: 1, rest: {value: 2, rest: null}},
list = objToList(obj, 'value', 'rest');
console.log(list);
function objToList(o, v, t, l) {
return o ? objToList(o[t], v, t, (l||[]).concat(o[v])) : (l||[])
}
.as-console-wrapper {
top: 0;
max-height: 100% !important;
}

sum values in object if multiple keys are the same JS

for example i have 5 objects:
{ row: aa, col: 1, value: 1 }
{ row: bb, col: 2, value: 1 }
{ row: bb, col: 3, value: 1 }
{ row: aa, col: 1, value: 1 }
{ row: aa, col: 2, value: 1 }
i want to sum values if row and col are the same, so the output should be:
{ row: aa, col: 1, value: 2 }
{ row: bb, col: 2, value: 1 }
{ row: bb, col: 3, value: 1 }
{ row: aa, col: 2, value: 1 }
thank you for your help!
tried this:
Sum javascript object propertyA values with same object propertyB in array of objects
You can do this with reduce() and one object to store keys.
var data = [
{ row: 'aa', col: 1, value: 1 },
{ row: 'bb', col: 2, value: 1 },
{ row: 'bb', col: 3, value: 1 },
{ row: 'aa', col: 1, value: 1 },
{ row: 'aa', col: 2, value: 1 }
]
var o = {}
var result = data.reduce(function(r, e) {
var key = e.row + '|' + e.col;
if (!o[key]) {
o[key] = e;
r.push(o[key]);
} else {
o[key].value += e.value;
}
return r;
}, []);
console.log(result)
Just for completeness, with a version for variable keys, an object for grouping the parts and Array#forEach.
var data = [{ row: 'aa', col: 1, value: 1 }, { row: 'bb', col: 2, value: 1 }, { row: 'bb', col: 3, value: 1 }, { row: 'aa', col: 1, value: 1 }, { row: 'aa', col: 2, value: 1 }],
grouped = [];
data.forEach(function (a) {
var key = ['row', 'col'].map(function (k) { return a[k]; }).join('|');
if (!this[key]) {
this[key] = { row: a.row, col: a.col, value: 0 };
grouped.push(this[key]);
}
this[key].value += a.value;
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
What I would do is put your objects in an array then iterate over that and check on each iteration if the key of a new object matches that of an old one and load the objects into a separate array if there isn't a match. If it does match then add its value to the value of the old own. I tested the following code and it seems to work how you want.
var array = [{ row: 'aa', col: 1, value: 1 },
{ row: 'bb', col: 2, value: 1 },
{ row: 'bb', col: 3, value: 1 },
{ row: 'aa', col: 1, value: 1 },
{ row: 'aa', col: 2, value: 1 }];
var newArray = [];
for(var x in array) {
for(var y in newArray) {
var found = false;
if(array[x].row == newArray[y].row && array[x].col == newArray[y].col) {
newArray[y].value += array[x].value;
found = true;
break;
}
}
if(!found) {
newArray.push(array[x]);
}
}
console.log(newArray);

Categories

Resources