Rename duplicates in an array JavaScript - javascript

I am asking for help to solve the problem renaming the strings in array that looks like this:
["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]
After the function execution it should look as follows:
["a(1)","a(6)","a","a(2)","a(3)","a(4)","a(5)","a(7)","a(8)","a(9)","a(10)","a(11)"]
Empty array and array free of duplicates should left untouched.
My idea is to populate an empty object with key/value pairs and then just push them to a new array:
function renameFiles(arr){
var itemsObj = {};
var count = 1;
for (var i = 0; i < arr.length; i++){
itemsObj[arr[i]] = count;
// if the key present, rename the array item and add it to the
// itemsObj
if (arr[i] in itemsObj){
itemsObj[arr[i] + '(' + (i - (i - 1)) + ')']
}
}
console.log(itemsObj)
// once the itmesObj is set, run the loop and push the keys to the
// array
return arr;
}
var array = ["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]
renameFiles(array);
The problem is that the itemsObj is not get populated with duplicates keys. There should be some other method that can handle this task. I am a beginner and probably not aware of that method.

You're almost there. You'd keep a count, and check for duplicates, and then do another check for duplicates with parentheses, and update the count appropriately
function renameFiles(arr){
var count = {};
arr.forEach(function(x,i) {
if ( arr.indexOf(x) !== i ) {
var c = x in count ? count[x] = count[x] + 1 : count[x] = 1;
var j = c + 1;
var k = x + '(' + j + ')';
while( arr.indexOf(k) !== -1 ) k = x + '(' + (++j) + ')';
arr[i] = k;
}
});
return arr;
}
var res = renameFiles(["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]);
console.log(res)
.as-console-wrapper {top:0; max-height:100%!important}

You were on the right track.
Another solution,
(function(){
var renameFiles = function(arr){
var counts = {}
for(var i=0;i<arr.length;i++){
if(!counts[arr[i]])
counts[arr[i]]=0;
counts[arr[i]]++;
}
arr = [];
for(var name in counts){
for(var i=0;i<counts[name];i++){
arr.push(name+(i===0?'':'('+i+')'));
}
}
return arr;
}
var array = ["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"];
console.log(renameFiles(array))
})();

var arr = ["a(1)","a(6)","a","a","a","a","a","a","a","a","a","a"]
function renameFiles(arr){
var dup_count, new_name;
arr.forEach((item, index) => {
dup_count = arr.filter(x => x == item).length;
if (dup_count > 1) {
for(n = 0; n < dup_count;){
do {
new_name = `${item}(${n+=1})`;
} while (arr.includes(new_name));
arr[arr.indexOf(item)] = new_name;
}
}
});
return arr
}
> renameFiles(arr)
< (12) ["a(1)", "a(6)", "a(2)", "a(3)", "a(4)", "a(5)", "a(7)", "a(8)", "a(9)", "a(10)", "a(11)", "a"]

To achieve the same result without mutation you can use the following code. Whilst it is much more code a majority of the functions I have included can be replaced with functions included with Ramda or another FP library. I find the following more readable, however that is simple a matter of preference.
Working sandbox here.
const incrementingList = (n) => [...Array(n).keys()];
const formatStr = (key) => ifElse(equals(0))(always(key))(concat(key));
const incValues = (obj) =>
flatMap((key) => map(formatStr(key))(incrementingList(obj[key])))(keys(obj));
const incOrInit = (record, key) =>
isNil(record[key])
? assoc(key)(1)(record)
: assoc(key)(inc(record[key]))(record);
const generateCounts = reduce({})(incOrInit);
const renameList = compose(incValues, generateCounts);
const list = ["a", "b", "b", "b", "a", "a", "c", "c", "c", "d"];
const result = renameList(list);
console.log(result); // => ["a", "a1", "a2", "b", "b1", "b2", "c", "c1", "c2", "d"]
// THESE FUNCTIONS CAN BE REPLACE WITH RAMDA \\
function keys(obj) {
return Object.keys(obj);
}
function ifElse(cond) {
return function ifTrueFn(trueFn) {
return function ifFalseFn(falseFn) {
return function passValue(value) {
return cond(value) ? trueFn(value) : falseFn(value);
};
};
};
}
function always(value) {
return function alwaysInner() {
return value;
};
}
function concat(a) {
return function inner(b) {
return a.concat(b);
};
}
function equals(a) {
return function equalsInner(b) {
return a === b;
};
}
function compose2(fn1, fn2) {
return function passArg(...args) {
return fn1(fn2(...args));
};
}
function compose(...fns) {
return fns.reduce(compose2);
}
function flatMap(mapFn) {
return function inner(list) {
return list.flatMap(mapFn);
};
}
function map(mapFn) {
return function passList(list) {
return list.map(mapFn);
};
}
function reduce(init) {
return function reducer(reducer) {
return function data(data) {
return data.reduce(reducer, init);
};
};
}
function assoc(key) {
return function assocValue(value) {
return function assocObject(obj) {
return { ...obj, [key]: value };
};
};
}
function inc(n) {
return n + 1;
}
function isNil(value) {
return value == null;
}

Related

Javascript how to optimise this count array values function

I have a function that mimics the array_count_values function from php in javascript but it's not very fast. I'm wondering if there's a way to fix it?
function array_count_values(arr) {
let a = [], prev;
arr.sort();
for ( let i = 0; i < arr.length; i++ ) {
if ( arr[i] !== prev ) {
a.push(1)
} else {
a[a.length-1]++;
}
prev = arr[i];
}
return a;
}
It just returns a simple array of numbers with the counts so like 2,1,2,1,1. The input in this case would be numeric arrays 5-7 elements long, so for example array_count_values([6,4,10,6,6])
You can use reduce to loop thru the array and count each entry.
function array_count_values(arr) {
return arr.reduce((c, v) => {
c[v] = c[v] || 0;
c[v]++;
return c;
}, {})
}
var result = array_count_values([6, 4, 10, 6, 6]);
console.log(result);
You could take an object for counting and omit sorting. This approach uses a single loop.
function array_count_values(array) {
var count = {},
i;
for (i = 0; i < array.length; i++) {
if (array[i] in count) {
count[array[i]]++;
} else {
count[array[i]] = 1;
}
}
return Object.values(count).sort((a, b) => b - a);
}
console.log(array_count_values([6, 4, 10, 6, 6]));
This is actually a straight-forward algorithm. I've been brushing up on them lately:
var array_count_values = function(array) {
let dict = {};
for (var i = 0; i < array.length; i++ ) {
var num = array[i];
(dict[num]) ? dict[num]++ : dict[num] = 1;
}
return dict;
}
console.log(array_count_values([6, 4, 10, 6, 6]));
Time and space complexity is both O(n).
I think the addition of a sort here is overkill, and probably the slowest part of this.
I think this will be the fastest/simplest way you can do this.
function array_count_values(arr) {
let outputCounts = {};
for ( let i = 0; i < arr.length; i++ ) {
if (outputCounts[arr[i]] != undefined){
outputCounts[arr[i]] += 1;
} else {
outputCounts[arr[i]] = 1;
}
}
return outputCounts;
}
The caveat here is that you're going to get an object back instead of an array as in your example.
const arr = [1, 2, 2, 3];
function array_count_values (arr) {
const frequencies = arr.reduce((f, v) => {
const freq = f.get(v) || 0;
f.set(v, freq + 1);
return f;
}, new Map());
return arr.map(v => frequencies.get(v));
}
console.log(array_count_values(arr));
Looking at how array_count_values works in php. This might be what you are looking for
function array_count_values(arr) {
return arr.reduce((acc, val) => {
if (!acc[val]) {
acc[val] = 0
}
acc[val] += 1
return acc
}, {})
}
To return an array as required in the question
function array_count_values(arr) {
return Object.values(arr.reduce((acc, val) => {
if (!acc[val]) {
acc[val] = 0
}
acc[val] += 1
return acc
}, {}))
}

How to get a object's value by a key list

I have a obj you see bellow:
var obj = {
a: {
b:{
c:"c"
}
}
}
and I want to get value like bellow :
var t = ["a", "b", "c"] // give a key list
console.log(obj["a"]["b"]["c"]) // and get value like this.
in depth, I want to encapsulate the requirement in a function:
function get_value(obj, key_list) {
}
because the key_list is not certain, so how to realize the function?
Just use a while loop and iterate over the keys
var obj = {
a: {
b:{
c:"c"
}
}
}
var t = ["a", "b", "c"];
function get_value(obj, key_list) {
let currentReference = obj;
while (key_list.length !== 0) {
currentReference = currentReference[key_list[0]];
key_list = key_list.slice(1);
}
return currentReference;
}
console.log(get_value(obj, t));
It'd be more elegant with reduce though:
var obj = {
a: {
b:{
c:"c"
}
}
}
var t = ["a", "b", "c"];
function get_value(obj, key_list) {
return key_list.reduce((currentReference, key) => currentReference[key], obj);
}
console.log(get_value(obj, t));
Recursive solution:
function get_value(obj, key_list, i) {
if (i == undefined) {
i = 0;
}
if (i < key_list.length - 1) {
return get_value(obj[key_list[i]], key_list, i+1)
}
return obj[key_list[i]]
}

Flatten a nested objects to one dimensional array javascript

I have a nested objects in this structure:
myArray = {
"D": {
"U": {
"A300": "B300",
"A326": "B326",
"A344": "B344",
"A345": "B345"
},
"P": {
"A664": "B664",
"A756": "B756"
}
},
"I": {
"U": {
"A300": "B300",
"A326": "B326"
},
"P": {
"A756": "B756"
}
}
};
I am trying to get the data out of it to be only one dimensional (Flatten). I tried the code below but it doesn't work:
var myNewArray = [].concat.apply([], myArray);
and
var myNewArray = myArray.reduce(function(prev, curr) {
return prev.concat(curr);
});
I want myNewArray to have ["B300","B326","B344","B345","B664","B756"]
You can do something like this:
var myArray = [];
myArray[0] = [];
myArray[0][0] = [];
myArray[0][0][0] = [];
myArray[0][0][1] = [];
myArray[0][1] = [];
myArray[0][1][0] = [];
myArray[0][0][0][0] = "abc1";
myArray[0][0][0][1] = "abc2";
myArray[0][0][1][0] = "abc3";
myArray[0][1][0][1] = "abc4";
myArray[0][1][0][1] = "abc5";
function flat(acc, val){
if(Array.isArray(val)){
acc = acc.concat(val.reduce(flat, []));
}else{
acc.push(val);
}
return acc;
}
var newMyArray = myArray.reduce(flat, []);
console.log(newMyArray);
What this does is to recursively reduce all the inner values that are arrays.
It seems that you're dealing with an object. The previous title of your question and the name of the variable are misleading.
In any case, flattening an object is a very similar process.
var myArray = {"D":{"U":{"A300":"B300","A326":"B326","A344":"B344","A345":"B345"},"P":{"A664":"B664","A756":"B756"}},"I":{"U":{"A300":"B300","A326":"B326"},"P":{"A756":"B756"}}};
function flatObj(obj){
return Object.keys(obj).reduce(function(acc, key){
if(typeof obj[key] === "object"){
acc = acc.concat(flatObj(obj[key]));
}else{
acc.push(obj[key]);
}
return acc;
}, []);
}
var newMyArray = flatObj(myArray);
console.log(newMyArray);
I just wanted to add my 2 cents since I was following this question and working on an answer before I left work. I'm home now so I want to post what I came up with.
const obj = {
x1: {
y1: {
z1: {
h1: 'abc',
h2: 'def'
},
z2: {
h1: 123,
h2: 456
}
}
}
}
const valAll = getPropValuesAll(obj)
console.log(valAll)
function getPropValuesAll(obj, result = []){
for(let k in obj){
if(typeof obj[k] !== 'object'){
result.push(obj[k])
continue
}
getPropValuesAll(obj[k], result)
}
return result
}
It would be easy and safe answer.
var myArray = [["abc1"],[["abc2",,"abc3"]],"abc4",{"r5": "abc5", "r6": "abc6"}];
var myNewArray = [];
function flatten(arr){
if(Array.isArray(arr)){
for(var i = 0, l = arr.length; i < l; ++i){
if(arr[i] !== undefined){
flatten(arr[i])
}
}
} else if (typeof arr === 'object') {
for(var key in arr){
if(arr.hasOwnProperty(key)){
flatten(arr[key])
}
}
} else {
myNewArray.push(arr)
}
}
flatten(myArray)
console.log(myNewArray)

How can i access this object key in this conditional?

Write a function countWords that, when given a string as an argument, returns an object where keys are the words in the string, and values are the number of occurrences of that word within the string:
function countWords(string){
string = string.split(" ");
var newObj = {};
for(var i = 0 ; i === newObj.string ; i++){
if(newObj['i'] === newObj[string]){
newObj[string[i]] = i ;
}
}
return newObj;
}
countWords("hello hello"); // => {"hello": 2}
countWords("Hello hello"); // => {"Hello": 1, "hello": 1}
countWords("The quick brown"); // => {"The": 1, "quick": 1, "brown": 1}
I realized since you do not need to count the index of the split string, you have to change the conditions from i < string.length to i === key value of of the objects. Why can't I access the strings with newObj.string?
You could do this with reduce() instead of for loop.
function countWords(string) {
return string.split(' ').reduce(function(r, e) {
r[e] = (r[e] || 0) + 1;
return r;
}, {})
}
console.log(countWords("hello hello"))
console.log(countWords("Hello hello"))
console.log(countWords("The quick brown"))
With for loop your code could go like this.
function countWords(string) {
var string = string.split(" ");
var newObj = {};
for (var i = 0; i < string.length; i++) {
newObj[string[i]] = (newObj[string[i]] || 0) + 1
}
return newObj;
}
console.log(countWords("hello hello"));
console.log(countWords("Hello hello"));
console.log(countWords("The quick brown"));
function countWords(string){
return string
.split(" ")
.reduce((acc, curr) => {
if (curr in acc) {
acc[curr]++
} else {
acc[curr] = 1
}
return acc
}, {})
}
console.log(countWords("hello hello"));
console.log(countWords("Hello hello"));
console.log(countWords("The quick brown"));
You can do it in the following way by using the hasOwnProperty function to check if a property exists in the JavaScript Object, if it does increment the count and if it doesn't then initialize the count to 1.
function countWords(data) {
var words = data.split(" ");
var item = {};
for (var i = 0; i < words.length; i++) {
var prop = words[i];
item.hasOwnProperty(prop) ? item[prop] ++ : item[prop] = 1;
}
console.log(item);
return item;
}
countWords("hello hello"); // => {"hello": 2}
countWords("Hello hello"); // => {"Hello": 1, "hello": 1}
countWords("The quick brown"); // => {"The": 1, "quick": 1, "brown": 1}
newObj[string[i]] = (newObj[string[i]] || 0) + 1
according to my understanding this statement is used to check for duplicity
suppose you crated an empty object and then stored the key value pair in it by using this statement "(newObj[string[i]] || 0) + 1" you are checking if the default value of the key is zero if its zero you then increment its value by 1 if there are multiple value for the same key by doing this you can get the unique value in the array
Example
const uniqueValues = arr => {
let newObj = {}
for (let val of arr){
newObj[val] = [newObj || 0] + 1;
}
return (Object.keys(newObj).length);
}
uniqueValues([1,1,1,1,1,2]);

Javascript Recursion for creating a JSON object

needing some advice on how to do this properly recursively.
Basically what I'm doing, is entering in a bunch of text and it returns it as JSON.
For example:
The text:
q
b
name:rawr
Returns:
[
"q",
"b",
{
"name": "rawr"
}
]
And the following input:
q
b
name:rawr:awesome
Would return (output format is not important):
[
"q",
"b",
{
"name": {
"rawr": "awesome"
}
}
]
How can I modify the following code to allow a recursive way to have objects in objects.
var jsonify = function(input){
var listItems = input, myArray = [], end = [], i, item;
var items = listItems.split('\r\n');
// Loop through all the items
for(i = 0; i < items.length; i++){
item = items[i].split(':');
// If there is a value, then split it to create an object
if(item[1] !== undefined){
var obj = {};
obj[item[0]] = item[1];
end.push(obj);
}
else{
end.push(item[0]);
}
}
// return the results
return end;
};
I don't think recursion is the right approach here, a loop could do that as well:
var itemparts = items[i].split(':');
var value = itemparts.pop();
while (itemparts.length) {
var obj = {};
obj[itemparts.pop()] = value;
value = obj;
}
end.push(value);
Of course, as recursion and loops have equivalent might, you can do the same with a recursive function:
function recurse(parts) {
if (parts.length == 1)
return parts[0];
// else
var obj = {};
obj[parts.shift()] = recurse(parts);
return obj;
}
end.push(recurse(items[i].split(':')));
Here is a solution with recursion:
var data = [];
function createJSON(input) {
var rows = input.split("\n");
for(var i = 0; i < rows.length; i++) {
data.push(createObject(rows[i].split(":")));
}
}
function createObject(array) {
if(array.length === 1) {
return array[0];
} else {
var obj = {};
obj[array[0]] = createObject(array.splice(1));
return obj;
}
}
createJSON("p\nq\nname:rawr:awesome");
console.log(data);

Categories

Resources