JavaScript: Iterate through multidimentional array vertically - javascript

I've got multidimentional array and I need to count chars vertically. No problem to count in row, but I can't iterate it like vertically. Tip please.
const arrayData = [
['a', 'b', 'c'],
['a', 'f', 'g'],
['b']
];
My code looks like this:
const countChars = (input, direction) => {
if (direction === 'row') {
return input.reduce((acc, curr) => {
acc[curr] = acc[curr] ? ++acc[curr] : 1;
return acc;
}, {});
}
if (direction === 'column') {
for (let row = 0; row < input.length; row++) {
for (let column = 0; column < input[row].length; column++) {
console.log(input[column][row]);
}
console.log('---');
}
}
}
But for columns I'm getting this as result:
a
a
b
---
b
f
undefined
---
c
So I'm losing there a char because of undefined.
The result should be like for columns:
{ 'a': 2, 'b': 1 }
{ 'b': 1, 'f': 1 }
{ 'c': 1, 'g': 1 }

You could iterate the array and collect same values at same index.
const
array = [['a', 'b', 'c'], ['a', 'f', 'g'], ['b']],
result = array.reduce((r, a) => {
a.forEach((v, i) => {
r[i] = r[i] || {};
r[i][v] = (r[i][v] || 0) + 1;
});
return r;
}, []);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

Find duplicate or falsy value in a JS object - Javascript

I have an object where it can contain a duplicate and/or a falsy value. I want to compose an array of objects based on that and add a new boolean property based on the check for case-insensitive values.
This is what I have:
const obj = {
a: 'A',
b: 'B',
c: 'C',
d: 'c',
e: 'E',
f: ''
}
console.log(Object.keys(obj).map(i => {
return {
key: i,
isDuplicateOrFalsy: _.filter(
Object.values(obj),
j =>
_.trimEnd(_.toLower(j)) ===
_.trimEnd(
_.toLower(
obj[i]
)
)
).length > 1 ||
!_.every(
Object.values(obj),
Boolean
)
}
}))
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
Expected Output:
[{
isDuplicateOrFalsy: false,
key: "a"
}, {
isDuplicateOrFalsy: false,
key: "b"
}, {
isDuplicateOrFalsy: true,
key: "c"
}, {
isDuplicateOrFalsy: true,
key: "d"
}, {
isDuplicateOrFalsy: false,
key: "e"
}, {
isDuplicateOrFalsy: true,
key: "f"
}]
Please advice.
Convert the object to entries of [key, value] with _.toPairs(), and group them by the lower case version of the value. Flat map the groups, and map each entry in the group back to an object. Any item within a group with length greater than 1 is a duplicate. Merge the objects, and get the items in the correct order using _.at():
const fn = obj => _.at(
_.merge(..._.flatMap(
_.groupBy(_.toPairs(obj), ([, v]) => _.lowerCase(v)),
group => group.map(([key, v]) => ( { [key]:{
key,
isDuplicateOrFalsy: group.length > 1 || _.isEmpty(_.trim(v))
}}))
)),
_.keys(obj)
)
const obj = {"a":"A","b":"B","c":"C","d":"C","e":"E","f":"","g":"c"}
const result = fn(obj)
console.log(result)
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>
You could do something similar to this:
const obj = { a: 'A', b: 'B', c: 'C', d: 'C', e: 'E', f: '' };
const res = Object.entries(obj)
.map(([key, val], i, arr) => ({
key,
isDuplicateOrFalsy: !val ||
arr.some(([k, v], j) =>
j !== i && v.toLowerCase().trim() === val.toLowerCase().trim()
)
}));
console.log(res);
Solution does not contain unnecessary cycles:
const obj = {
a: 'A',
b: 'B',
c: 'C',
d: 'C',
e: 'E',
f: ''
}
// make array [ [key, value], ... ] and sort by values
values = Object.entries(obj).sort((a,b) => a[1] > b[1])
result = values.map((e,i, arr) => {
const [key, value] = e;
const last = values.length -1; // last index
let isDuplicateOrFalsy = false;
// true conditions = dublicates are near
if (!value) isDuplicateOrFalsy = true; // falsy check
else if (i > 0 && i < last // for middle
&& (arr[i-1][1] === value || arr[i+1][1] === value)) isDuplicateOrFalsy = true;
else if (i === 0 && arr[1][1] === value) isDuplicateOrFalsy = true; // for first
else if (i === last && arr[last-1][1] === value) isDuplicateOrFalsy = true; // for last
return {
key,
isDuplicateOrFalsy
}
})
console.log(result)
const obj = {
a: 'A',
b: 'B',
c: 'C',
d: 'c',
e: 'E',
f: ''
};
const isDuplicateOrFalsyByValue = Object
.values(obj)
.reduce(
(result, value) => {
const caseInsensetiveValue = value.toLowerCase();
result[caseInsensetiveValue] = result[caseInsensetiveValue] === undefined
/*
* If `caseInsensetiveValue` is a falsy value,
then set `isDuplicateOrFalsy` to `true`
* Otherwise set it to `false`
*/
? !caseInsensetiveValue
/*
* If result[caseInsensetiveValue] is `true` (we had a falsy value),
then this `true` won't hurt
* Otherwise we have a duplicate at this point
and should set it to `true` as well.
*/
: true;
return result;
},
{},
);
const keysWithDuplicationOrFalsyInfo = Object
.entries(obj)
.reduce(
(result, [key, value]) => [
...result,
{
isDuplicateOrFalsy: isDuplicateOrFalsyByValue[value.toLowerCase()],
key,
},
],
[],
);
console.log('keysWithDuplicationOrFalsyInfo');
console.log(keysWithDuplicationOrFalsyInfo);
A short, and more human readable.
const obj = {
a: 'A',
b: 'B',
c: 'C',
d: 'c',
e: 'E',
f: ''
}
// Object map number of occurance of each value. { a: 1, b: 1, c: 2, d: 1 }
const valuesOccurance = _.mapValues(_.groupBy(obj, _.lowerCase), occurances => occurances.length);
// function to check duplicate
const isDuplicate = value => valuesOccurance[_.lowerCase(value)] > 1;
// function to check falsy value
const isFalsy = value => !value;
const result = _.map(obj, (value, key) => {
return {
isDuplicateOrFalsy: isFalsy(value) || isDuplicate(value),
key,
};
});
console.log({ result })
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.20/lodash.min.js"></script>

Remove duplicated combination in array based on index

I have the following data array:
const data = [
{
value: [
'a',
'b',
'a',
'a'
]
},
{
value: [
'c',
'c',
'd',
'c'
]
}
];
So there's is 4 combination here based on index:
combination 1 : a - c (index 0 in each value arrays)
combination 2 : b - c (index 1 in each value arrays)
combination 3 : a - d (index 2 in each value arrays)
combination 4 : a - c (index 3 in each value arrays)
As you can see the first and the last combinations are the same, so i want to remove the second occurrence from each array, the result should be:
[
{
value: [
'a',
'b',
'a'
]
},
{
value: [
'c',
'c',
'd'
]
}
]
You can zip the values arrays from both objects to form an array which looks like:
["a-c", "b-c", ...]
As these are now strings, you can turn this array into a Set using new Set(), which will remove all duplicate occurrences. You can then turn this set back into an array which you can then use .reduce() on to build you array of objects from. For each value you can obtain the list of values by using .split() on the '-', and from that, populate your reduced array.
See example below:
const data = [{ value: [ 'a', 'b', 'a', 'a' ] }, { value: [ 'c', 'c', 'd', 'c' ] } ];
const unq = [...new Set(
data[0].value.map((_,c)=> data.map(({value})=>value[c]).join('-'))
)];
const res = unq.reduce((acc, str) => {
const values = str.split('-');
values.forEach((value, i) => acc[i].value.push(value));
return acc;
}, Array.from({length: data.length}, _ => ({value: []})))
console.log(res);
Limitations of the above method assume that you won't have a - character as your string value. If this is an issue, you can consider using a different delimiter, or find unique values within your array using .filter() instead of a Set.
You could save a lookup object for unique pairs of value based with index
Given your input is, below solution could help you
const data = [
{
value: ["a", "b", "a", "a"],
},
{
value: ["c", "c", "d", "c"],
},
]
const lookup = {}
data[0].value.forEach((_, index) => {
lookup[`${data[0].value[index]}-${data[1].value[index]}`] = true
})
const res = Object.keys(lookup).reduce(
(acc, key) => {
const [val1, val2] = key.split("-")
acc[0].value.push(val1)
acc[1].value.push(val2)
return acc
},
[{ value: [] }, { value: [] }]
)
console.log(res)
Below is a two step solution with a generator function and a single pass.
const data = [ { value: [ 'a', 'b', 'a', 'a' ] }, { value: [ 'c', 'c', 'd', 'c', ] } ];
const zipDataValues = function* (data) {
const iterators = data.map(item => item.value[Symbol.iterator]())
let iterations = iterators.map(iter => iter.next())
while (iterations.some(iteration => !iteration.done)) {
yield iterations.map(iteration => iteration.value)
iterations = iterators.map(iter => iter.next())
}
}
const filterOutDuplicateCombos = function (values) {
const combosSet = new Set(),
resultData = [{ value: [] }, { value: [] }]
for (const [valueA, valueB] of values) {
const setKey = [valueA, valueB].join('')
if (combosSet.has(setKey)) {
continue
}
combosSet.add(setKey)
resultData[0].value.push(valueA)
resultData[1].value.push(valueB)
}
return resultData
}
console.log(
filterOutDuplicateCombos(zipDataValues(data))
) // [ { value: [ 'a', 'b', 'a' ] }, { value: [ 'c', 'c', 'd' ] } ]
Here is a reference on generators and iterators
Filter combinations + sorting by the first occurrence:
const data = [{
value: ['a', 'b', 'a', 'a']
},{
value: ['c', 'c', 'd', 'c']
}];
var res = {}, i, t;
for (i = 0; i < data[0].value.length; ++i) {
res[data[0].value[i]] = res[data[0].value[i]] || {};
res[data[0].value[i]][data[1].value[i]] = true;
}
data[0].value = [];
data[1].value = [];
for (i in res) {
for (t in res[i]) {
data[0].value[data[0].value.length] = i;
data[1].value[data[1].value.length] = t;
}
}
console.log(data);

JavaScript Reduce an array to find match in Object

I am trying to incorporate array method: reduce.
Basically, what I am trying to accomplish here is to reduce the array below to an object where anything that matches obj's key value.
const arr = ['a', 'c', 'e'];
const obj = { a: 1, b: 2, c: 3, d: 4 };
let output = select(arr, obj);
console.log(output); // --> { a: 1, c: 3 }
My select method:
function select(arr, obj) {
let newObj = {};
for (let prop in obj) {
for (let i = 0; i < arr.length; i++) {
if (prop === arr[i]) {
newObj[prop] = obj[prop];
}
}
}
return newObj;
}
I set {} as initializer for arr.reduce as such if current value of array matches key of object then it would add to accumulator the key value, but I am receiving an error message from the console that if expression cannot return boolean.
Here is my attempt using .reduce():
function select(arr, obj) {
let result = arr.reduce(function(x, y) {
if (y in obj) {
x[y] = obj[y]
return x;
}
}, {})
return result;
}
Please advise.
You must always return the accumulator. Here is how to use reduce
function select(arr, obj) {
return arr.reduce(function (acc, key) {
if (key in obj) acc[key] = obj[key];
return acc;
}, {});
}
const arr = ['a', 'c', 'e'];
const obj = { a: 1, b: 2, c: 3, d: 4 };
let output = select(arr, obj);
console.log(output); // --> { a: 1, c: 3 }
The accumulator should be returned in all the cases.
I used an implementation using a filter for your reference:
const arr = ['a', 'c', 'e'];
const obj = { a: 1, b: 2, c: 3, d: 4 };
function select (obj,arr){
let newObj = Object.keys(obj).filter(key => arr.includes(key)).reduce((acc,key) => {
acc[key]=obj[key]
return acc
},{})
return newObj
}
console.log(select(obj,arr));
function select(arr, obj) {
return arr.reduce((acc, curr) => {
if(obj[curr]) {
acc[curr] = obj[curr];
}
return acc;
}, {})
}

Form nested JSON object array from given JSON array

I have a json array of the form
[{'from':'a','to':'b','type':'add','value':'100','op':'cr'},
{'from':'a','to':'b','type':'add','value':'200','op':'dr'},
{'from':'a','to':'b','type':'add','value':'300','op':'cr'},
{'from':'c','to':'d','type':'sub','value':'400','op':'dr'},
{'from':'c','to':'d','type':'sub','value':'500','op':'cr'}]
I want the output as
[{'from':'a','to':'b','add':[{'100':'cr'},{'200':'dr'},{'300':'cr'}]},
{'from':'c','to':'d','sub':[{'400':'dr'},{'500':'cr'}]}]
How to do it in Javascript/NodeJS?
You could use an object as hash table and assign the values via the key with parts from from and to.
var data = [{ from: 'a', to: 'b', option: '100' }, { from: 'a', to: 'b', option: '200' }, { from: 'a', to: 'b', option: '300' }, { from: 'c', to: 'd', option: '400' }, { from: 'c', to: 'd', option: '500' }],
grouped = [];
data.forEach(function (a) {
var key = [a.from, a.to].join('|');
if (!this[key]) {
this[key] = { from: a.from, to: a.to, option: [] };
grouped.push(this[key]);
}
this[key].option.push(a.option);
}, Object.create(null));
console.log(grouped);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Try the following code snippet -
'use strict';
var x = [{ 'from': 'a', 'to': 'b', 'option': '100' },
{ 'from': 'a', 'to': 'b', 'option': '200' },
{ 'from': 'a', 'to': 'b', 'option': '300' },
{ 'from': 'c', 'to': 'd', 'option': '400' },
{ 'from': 'c', 'to': 'd', 'option': '500' }
];
var match = false;
x.reduce(function(returnVal, item) {
match = false;
returnVal.map(function(each) {
if (each.from === item.from && each.to === item.to) {
if (Array.isArray(each.option)) {
each.option.push(item.option);
} else {
each.option = [each.option];
each.option.push(item.option);
}
match = true;
}
return each;
})
if (!match) {
returnVal.push(item);
}
return returnVal;
}, []);
Using a simple loop to iterate through keys in a 'tmp' object which combined from 'from' and 'to' may help:
var input = [{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{ 'from': 'c', 'to': 'd', 'option': '500' }];
var tmp = {};
$.each(input, function (idx, obj) {
var key = obj.from + obj.to
tmp[key] = tmp[key] || { from: obj.from, to: obj.to};
tmp[key].option = tmp[key].option || [];
tmp[key].option.push(obj.option);
});
var output = [];
for(var key in tmp)
{
output.push(tmp[key]);
}
Wihtout jQuery, only javascript and cross browswer:
var array = [
{
'from': 'a',
'to': 'b',
'option': '100'
},
{
'from': 'a',
'to': 'b',
'option': '200'
},
{
'from': 'a',
'to': 'b',
'option': '300'
},
{
'from': 'c',
'to': 'd',
'option': '400'
},
{
'from': 'c',
'to': 'd',
'option': '500'
}
];
var array2 = [];
for (var a in array) {
for (var b in array2) {
if (array2[b].from == array[a].from && array2[b].to == array[a].to) {
array2[b].option.push(array[a].option);
break;
}
}
if (!array2[b] || array2[b].option.indexOf(array[a].option) == -1) {
array2.push({
from: array[a].from,
to: array[a].to,
option: [array[a].option]
});
}
}
console.log(array2);
You can use the below function unique to get the unique array out of the given array.
var array1 = [
{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{'from':'c','to':'d','option':'500'}
];
function unique(array) {
var i = 0,
map = {}, // This map object stores the objects of array1 uniquely
uniqueArray = [],
obj,
len = array.length;
for (i = 0; i < len; i++) {
obj = array[i];
from = obj.from; to = obj.to;
// Create an id using from and to of the object
id = from + '-' + to;
// Check if object is already there in map object
if (map[id]) {
// If options is not an array then store the options in array
map[id].option = map[id].option instanceof Array ? map[id].option : [map[id].option];
map[id].option.push(obj.option);
}
// If object not available in map then create an object
else {
map[id] = {};
map[id].from = obj.from;
map[id].to = obj.to;
map[id].option = obj.option;
// Pushing the map object to the unique array
uniqueArray.push(map[id]);
}
}
return uniqueArray;
}
var array1 = [
{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{'from':'c','to':'d','option':'500'}
];
var array2 = [];
for(var i=0; i<array1.length; i++) {
var obj = null,
from = array1[i]['from'],
to = array1[i]['to'];
for(var j=0; j<array2.length; j++) {
if (array2[j]['from'] == from && array2[j]['to'] == to) {
obj = array2[j];
break;
}
}
if (obj == null) {
obj = {'from':from,'to':to,'option':[]};
array2.push(obj);
}
obj['option'].push(array1[i]['option']);
}
console.log(array2);
JS Fiddle: https://jsfiddle.net/6wqkhms3/1/
var data = [{'from':'a','to':'b','option':'100'},
{'from':'a','to':'b','option':'200'},
{'from':'a','to':'b','option':'300'},
{'from':'c','to':'d','option':'400'},
{'from':'c','to':'d','option':'500'}];
var out = [];
// Utility function which finds-out if this object is available in the RESULT array or not
function findObj(list, item) {
var resultObj;
for (var i in list) {
if (list[i].from === item.from && list[i].to === item.to) {
resultObj = list[i];
break;
}
}
return resultObj;
}
// EXECUTION
for (var i in data) {
// Check if this objec is available in the RESULT array,
if (findObj(out, data[i])) {
// If yes, then push the value to it
findObj(out, data[i]).option.push(data[i].option);
} else {
// If NO, then add this item to the RESULT array
out.push({
from: data[i].from,
to: data[i].to,
option: [data[i].option]
});
}
}
console.log(out);

Is there a Javascript function similar to the Python Counter function?

I am attempting to change a program of mine from Python to Javascript and I was wondering if there was a JS function like the Counter function from the collections module in Python.
Syntax for Counter
from collection import Counter
list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a']
counter = Counter(list)
print counter
output
Counter({'a':5, 'b':3, 'c':2})
DIY JavaScript solution:
var list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
function Counter(array) {
var count = {};
array.forEach(val => count[val] = (count[val] || 0) + 1);
return count;
}
console.log(Counter(list));
JSFiddle example
Update:
Alternative that uses a constructor function:
var list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
function Counter(array) {
array.forEach(val => this[val] = (this[val] || 0) + 1);
}
console.log(new Counter(list));
JSFiddle example
You can use Lo-Dash's countBy function:
var list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
console.log(_.countBy(list));
JSFiddle example
I know I'm late but in case if someone is looking at this in 2020 you can do it using reduce, for example:
const counter = (list) => {
return list.reduce(
(prev, curr) => ({
...prev,
[curr]: 1 + (prev[curr] || 0),
}),
{}
);
};
console.log(counter([1, 2, 1, 1, 1, 1, 2, 3, 4, 5, 6, 7, 1, 0]));
// output -> { '0': 1, '1': 6, '2': 2, '3': 1, '4': 1, '5': 1, '6': 1, '7': 1 }
more advance example with a callback function and context binding
const data = [1, 2, 3, 4, 5];
const counter = (list, fun, context) => {
fun = context ? fun.bind(context) : fun;
return list.reduce((prev, curr) => {
const key = fun(curr);
return {
...prev,
[key]: 1 + (prev[key] || 0),
};
}, {});
};
console.log(counter(data, (num) => (num % 2 == 0 ? 'even' : 'odd')));
// output -> { odd: 3, even: 2 }
There is also pycollections.js, which works on Node and in client-side JS.
Example:
var collections = require('pycollections');
var counter = new collections.Counter([true, true, 'true', 1, 1, 1]);
counter.mostCommon(); // logs [[1, 3], [true, 2], ['true', 1]]
For those who want a pure JavaScript solution:
function countBy (data, keyGetter) {
var keyResolver = {
'function': function (d) { return keyGetter(d); },
'string': function(d) { return d[keyGetter]; },
'undefined': function (d) { return d; }
};
var result = {};
data.forEach(function (d) {
var keyGetterType = typeof keyGetter;
var key = keyResolver[keyGetterType](d);
if (result.hasOwnProperty(key)) {
result[key] += 1;
} else {
result[key] = 1;
}
});
return result;
}
Therefore:
list1 = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
console.log(countBy(list1)); // {'a':5, 'b':3, 'c':2}
list2 = ['abc', 'aa', 'b3', 'abcd', 'cd'];
console.log(countBy(list2, 'length')); // {2: 3, 3: 1, 4: 1}
list3 = [1.2, 7.8, 1.9];
console.log(countBy(list3, Math.floor)); // {1: 2, 7: 1}
In Python, the Counter also has add and update methods, which are used quite commonly. So a better solution would be this:
function Counter(array) {
this.add = (val) => {
this[val] = (this[val] || 0) + 1;
};
this.update = (array) => {
array.forEach((val) => this.add(val));
};
this.update(array);
}
// Example usage
let myCounter = new Counter([1, 2, 2])
myCounter.update([3, 3, 3])
myCounter.add(4)
console.log(myCounter)
Here is a simple and easy to read solution:
const word1 = "tangram"
const dict1 = {}
for (let char of word1){
console.log(char)
if (dict1[char]){
dict1[char] += 1
}else{
dict1[char]= 1
}
}
This is my solution with explicit function calls
let list = [4, 6, 5, 3, 3, 1];
function counter(list) {
let count = function(n) {
let cnt = 0;
for (let v of list) {
if (v === n) cnt++
}
return cnt
}
let [...listSet] = new Set(list);
let cntr = {};
for (let v of listSet) {
cntr[v] = count(v)
}
return cntr
}
console.log(counter(list))
Another version ->
s = "naman";
const counter = (s, sMap = {}) => {
[...s].map((el) => {
sMap[el] = sMap[el] ? sMap[el] + 1 : 1;
});
return sMap;
};
const res = counter(s);
console.log(`res`, res);
How about this pure functional way:
let list = ['a', 'b', 'c', 'b', 'a', 'b', 'c', 'a', 'a', 'a'];
function counter(array) {
return array.reduce((acc, value, index) => {
acc[value] = value in acc ? acc[value] + 1: 1
return acc;
}, {});
}
Fiddle Link

Categories