Advanced-Frontend / Daily-Interview-Question

我是依扬(木易杨),公众号「高级前端进阶」作者,每天搞定一道前端大厂面试题,祝大家天天进步,一年后会看到不一样的自己。

Home Page:https://muyiy.cn/question/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

第 125 题:如何将 [{id: 1}, {id: 2, pId: 1}, ...] 的重复数组(有重复数据)转成树形结构的数组 [{id: 1, child: [{id: 2, pId: 1}]}, ...] (需要去重)

yygmind opened this issue · comments

哈希表,时间复杂度O(n)

const fn = arr => {
  const res = []
  const map = arr.reduce((res, item) => ((res[item.id] = item), res), {})
  for (const item of Object.values(map)) {
    if (!item.pId) {
      res.push(item)
    } else {
      const parent = map[item.pId]
      parent.child = parent.child || []
      parent.child.push(item)
    }
  }
  return res
}

// const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
// fn(arr) => [{id: 1, child: [{id: 2, pId: 1, child: [{ id: 3, pId: 2}]}]}, {id: 4, child: [{id: 5, pId: 4}]}]
commented

是这个意思吗,不太清楚,轻喷哈

 //    如何将 [{id: 1}, {id: 2, pId: 1}, ...] 的重复数组(有重复数据)转成树形结构的数组 [{id: 1, child: [{id: 2, pId: 1}]}, ...] (需要去重)
      let arr = [
        { id: 1 },
        { id: 2, pid: 1 },
        { id: 3, pid: 2 },
        { id: 4, pid: 1 },
        { id: 5, pid: 3 },
        { id: 6, pid: 2 },
        { id: 6, pid: 2 },
        { id: 2, pid: 1 }
      ]
      function changeSet(arr) {
        let flatArr = [...new Set(arr.map(item => JSON.stringify(item)))].map(
          item => JSON.parse(item)
        )
        let res = flatArr.reduce((prev, item) => {
          if (item.pid) {
            let father = flatArr.find(cur => cur.id == item.pid)
            father.child ? father.child.push(item) : (father.child = [item])
            return prev
          } else {
            return (item.child = []), prev.push(item), prev
          }
        }, [])
      }
      let res = changeSet(arr)
      console.log(res)

var flatLs = [
{ id: 1},
{ id: 2},
{ id: 3},
{ id: 4, pid: 1 },
{ id: 5, pid: 3 },
{ id: 6, pid: 2 },
{ id: 7, pid: 4 },
{ id: 8, pid: 3 },
{ id: 9, pid: 4 }
]
const flat2tree = flatLs => {
flatLs.forEach(item => {
if (!item.pid) {
item.pid = 0;
} else {
const index = flatLs.findIndex(_item => _item.id === item.pid)
if (index !== -1) {
flatLs[index].children = flatLs[index].children || []
flatLs[index].children.push(item)
}
}
})
return flatLs.filter(item => item.pid === 0)
}
为什么代码没有缩进,[笑哭]

commented

哈希表,时间复杂度O(n)

const fn = arr => {
  const res = []
  const map = arr.reduce((res, item) => ((res[item.id] = item), res), {})
  for (const item of Object.values(map)) {
    if (!item.pId) {
      res.push(item)
    } else {
      const parent = map[item.pId]
      parent.child = parent.child || []
      parent.child.push(item)
    }
  }
  return res
}

// const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
// fn(arr) => [{id: 1, child: [{id: 2, pId: 1, child: [{ id: 3, pId: 2}]}]}, {id: 4, child: [{id: 5, pId: 4}]}]

@ZodiacSyndicate 👍 厉害!(提个小建议,最好复制一下对象,否则会修改原数组)

const map = arr.reduce((res, item) => ((res[item.id] = Object.assign({}, item)), res), {})
const arr = [
  { id: 1, pid: null },
  { id: 2, pid: 1 },
  { id: 3, pid: 1 },
  { id: 4, pid: 2 },
  { id: 5, pid: 2 },
  { id: 6, pid: 3 },
  { id: 7, pid: 3 },
  { id: 8, pid: 3 },
  { id: 9, pid: 8 },
  { id: 10, pid: 6 }
];

function arr2tree(arr = []) {
  let rootNode = arr.find(v => v.pid === null);
  if (!rootNode) {
    throw new Error("Expected rootNode not exists");
  }
  let rootNodeIndex = arr.findIndex(v => v.pid === null);
  arr.splice(rootNodeIndex, 1);
  let tree = {
    id: rootNode.id,
    children: []
  };
  let map = {};
  arr.forEach(v => {
    if (!map[v.pid]) {
      map[v.pid] = [];
    }
    map[v.pid].push(v);
  });

  for (const pid in map) {
    let children = map[pid];
    children.forEach(child => {
      if (map[child.id]) {
        child.children = map[child.id];
      }
    });
  }
  tree.children = map[rootNode.id];
  return tree;
}

const tree = arr2tree(arr);
commented
function convertToTree(arr) {
  const MAP = arr.reduce((res, cur) => res.set(cur.id, cur), new Map());
  return [...MAP].reduce((result, [, value]) => {
    const { pId } = value;
    if (pId === undefined) {
      result.push(value)
    } else {
      const parent = map.get(pId);
      parent && (parent.children || (parent.children = [])).push(value);
    }
    return result;
  }, [])
}

题目都看不懂,pid指父元素的id吗

我来写一个

function recursive(pItem,cArr) {
	let findArr = cArr.filter(item=>item.pId === pItem.id)
	if(findArr.length) {
	   for(let findItem of findArr) {
		delete findItem.pId
		recursive(findItem,cArr)
	   }
	   pItem.children = findArr
	}
 }
 
 function compose(arr) {	
	arr.filter((item,index,array)=>array.findIndex(cell=>cell.id===item.id)===index) //去重
	let pArr = arr.filter(item=>!item.pId)
	let cArr = arr.filter(item=>item.pId)
	for(let pItem of pArr) {
		recursive(pItem,cArr)
	}
	return pArr
 }

const fn = arr => {
let res = [];
let map = arr.reduce((res, item) => ((res[item.id] = item), res), {});
for (const item of Object.values(map)) {
if (item.pId) {
map[item.pId].child = map[item.pId].child || [];
map[item.pId].child.push(item);
} else {
res.push(item);
}
}
return res;
};

arrayToTree([...new Set(array.map(JSON.stringify))].map(JSON.parse))

function arrayToTree(data, nodesField, idField, parentIdField) {

if (!nodesField) nodesField = 'children';
idField = idField || '_id';
parentIdField = parentIdField || '_pid';

var nodes = [];


var idHash = {};
for (var i = 0, l = data.length; i < l; i++) {
    var o = data[i];
    if (!o) continue;
    var id = o[idField];
    if (id !== null && id !== undefined) {
        idHash[id] = o;
    }
    delete o[nodesField];
}


for (var i = 0, l = data.length; i < l; i++) {
    var o = data[i];
    var p = idHash[o[parentIdField]];
    if (!p) {
        nodes.push(o);
        continue;
    }
    if (!p[nodesField]) {
        p[nodesField] = [];
    }
    p[nodesField].push(o);
}
return nodes;

}

Object.values(map) 将对象转为数组
学习了

网站类目管理需求?

commented

@ZodiacSyndicate

reduce 遍历的时间复杂度不算吗?

function transformTree(arr) {
  let res = [];
  let map = arr.reduce((pre, item) => {//去重且进行构建哈希表
    pre[item.id] = item;
    return pre;
  }, {})
  console.log(map)
  for (let item of Object.values(map)) {
    if (!item.pId) {
      res.push(item);
    } else {
      const parent = map[item.pId];
      parent.child = parent.child || [];//当这个元素没有时指向一个数组并将该孩子元素增加进去含有有自己孩子时采用本来的child数组,
      parent.child.push(item)
    }
  }
  return res;
}
console.log(transformTree(arr))

var flatLs = [
{ id: 1},
{ id: 2},
{ id: 3},
{ id: 4, pid: 1 },
{ id: 5, pid: 3 },
{ id: 6, pid: 2 },
{ id: 7, pid: 4 },
{ id: 8, pid: 3 },
{ id: 9, pid: 4 }
]
const flat2tree = flatLs => {
flatLs.forEach(item => {
if (!item.pid) {
item.pid = 0;
} else {
const index = flatLs.findIndex(_item => _item.id === item.pid)
if (index !== -1) {
flatLs[index].children = flatLs[index].children || []
flatLs[index].children.push(item)
}
}
})
return flatLs.filter(item => item.pid === 0)
}
为什么代码没有缩进,[笑哭]

用Markdown语法写就有缩进了

Object.values(map) 将对象转为数组
学习了

Object.values()只是获取对象可枚举属性的value值,返回一个数组

哈希表,时间复杂度O(n)

const fn = arr => {
  const res = []
  const map = arr.reduce((res, item) => ((res[item.id] = item), res), {})
  for (const item of Object.values(map)) {
    if (!item.pId) {
      res.push(item)
    } else {
      const parent = map[item.pId]
      parent.child = parent.child || []
      parent.child.push(item)
    }
  }
  return res
}

// const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
// fn(arr) => [{id: 1, child: [{id: 2, pId: 1, child: [{ id: 3, pId: 2}]}]}, {id: 4, child: [{id: 5, pId: 4}]}]
const map = arr.reduce((res, item) => ((res[item.id] = item), res), {})

等同于

const map = {}
arr.forEach((item) => {
    map[item.id] = item
})

我差点还没看懂,太菜了233

/**
 * @desc
 *
 * @使用场景
 *
 * @coder.yang2010@gmail.com
 * @Date    2019/8/28
 **/

let arr: {id: number; pid?: number}[] = [
  {id: 1},
  {id: 2, pid: 1},
  {id: 3, pid: 2},
  {id: 4, pid: 1},
  {id: 5, pid: 3},
  {id: 6, pid: 2},
  {id: 6, pid: 2},
  {id: 2, pid: 1},
];

interface IItem {
  id: number;
  pid?: number;
  child?:IItem[];
}

let ItemsMap: {[id: number]: IItem} = {};
let PidItemsMap: {[pid: number]: IItem[]} = {};
for (let entry of arr) {
  if(ItemsMap[entry.id] ) {
    //去重!!
    continue;
  }
  ItemsMap[entry.id] = entry;

  let _pid = entry.pid || 'master';

  if (!PidItemsMap[_pid]) {
    PidItemsMap[_pid] = [entry];
  } else {
    PidItemsMap[_pid].push(entry);
  }
}

let root = PidItemsMap['master'];

for(let entry  of Object.values(ItemsMap)) {
  entry.child = PidItemsMap[entry.id];
}

console.log(JSON.stringify(root,null,2));
const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
function fn (arr) {
    const cache = {}
    const res = []
    for (let index in arr) {
        const item = arr[index]
        const cacheItem = cache[item.id]
        if (!cacheItem || typeof cacheItem.id == 'undefined') {
            const refItem = cacheItem || Object.assign({}, item)
            const refParent = cache[refItem.pId] || {}
            refParent.children = refParent.children || []
            refParent.children.push(refItem)  
            cache[item.pId] = refParent
            if (cacheItem) {
                Object.assign(refItem, item)
            } else {
                cache[item.id] = refItem
            }
            !item.pId && res.push(refItem)
        }
    }
    return res
}
fn(arr)



[
    {
        "id": 1,
        "children": [
            {
                "id": 2,
                "pId": 1,
                "children": [
                    {
                        "id": 3,
                        "pId": 2
                    }
                ]
            }
        ]
    },
    {
        "id": 4,
        "children": [
            {
                "id": 5,
                "pId": 4
            }
        ]
    }
]
  function formatArr (arr,newArr = []){
     //去重和第一次格式化数组
     let idArr = [],obj={}
     for(let i=0;i<arr.length;i++){
         let val = arr[i]        
        if(!~idArr.indexOf(val.id)){
            idArr.push( val.id ) 
            obj[val.id] = val
           !val.pId &&  newArr.push(val) 
        }
     }
     for(let i in obj ){
         if(obj[i].pId){
            obj[obj[i].pId].children ? obj[ obj[i].pId].children.push(obj[i]) :  obj[obj[i].pId].children = [ obj[i] ]
         }
     }
     return newArr
  }
commented

哈希表,时间复杂度O(n)

const fn = arr => {
  const res = []
  const map = arr.reduce((res, item) => ((res[item.id] = item), res), {})
  for (const item of Object.values(map)) {
    if (!item.pId) {
      res.push(item)
    } else {
      const parent = map[item.pId]
      parent.child = parent.child || []
      parent.child.push(item)
    }
  }
  return res
}

// const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
// fn(arr) => [{id: 1, child: [{id: 2, pId: 1, child: [{ id: 3, pId: 2}]}]}, {id: 4, child: [{id: 5, pId: 4}]}]

@ZodiacSyndicate 👍 厉害!(提个小建议,最好复制一下对象,否则会修改原数组)

const map = arr.reduce((res, item) => ((res[item.id] = Object.assign({}, item)), res), {})

Object.assign是浅拷贝

commented

哈希表,时间复杂度O(n)

const fn = arr => {
  const res = []
  const map = arr.reduce((res, item) => ((res[item.id] = item), res), {})
  for (const item of Object.values(map)) {
    if (!item.pId) {
      res.push(item)
    } else {
      const parent = map[item.pId]
      parent.child = parent.child || []
      parent.child.push(item)
    }
  }
  return res
}

// const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
// fn(arr) => [{id: 1, child: [{id: 2, pId: 1, child: [{ id: 3, pId: 2}]}]}, {id: 4, child: [{id: 5, pId: 4}]}]

@ZodiacSyndicate 👍 厉害!(提个小建议,最好复制一下对象,否则会修改原数组)

const map = arr.reduce((res, item) => ((res[item.id] = Object.assign({}, item)), res), {})

就是应用到了 引用
const fn = arr => {
const res = []
const map = arr.reduce((res, item) => {
// console.log('====res, item====', JSON.stringify(res), JSON.stringify(item));
res[item.id] = item;
// console.log('====res====', JSON.stringify(res));
return res;
}, {})
console.log('=====map====', JSON.stringify(map));
console.log('=====values====', JSON.stringify(Object.values(map)));
for (const item of Object.values(map)) {
if (!item.pId) {
res.push(item);
console.log('====res===', JSON.stringify(res))
} else {
const parent = map[item.pId];
console.log('====parent=====',JSON.stringify(parent))
parent.child = parent.child || [];
parent.child.push(item)
console.log('====parent====',JSON.stringify(parent))
console.log(JSON.stringify(res))
}
}
return res
}
const arr = [{id: 1}, {id:2, pId: 1}, {id: 3, pId: 2}, {id: 4}, {id:3, pId: 2}, {id: 5, pId: 4}]
fn(arr);
VM61519:9 =====map==== {"1":{"id":1},"2":{"id":2,"pId":1},"3":{"id":3,"pId":2},"4":{"id":4},"5":{"id":5,"pId":4}}
VM61519:10 =====values==== [{"id":1},{"id":2,"pId":1},{"id":3,"pId":2},{"id":4},{"id":5,"pId":4}]
VM61519:14 ====res=== [{"id":1}]
VM61519:17 ====parent===== {"id":1}
VM61519:20 ====parent==== {"id":1,"child":[{"id":2,"pId":1}]}
VM61519:21 [{"id":1,"child":[{"id":2,"pId":1}]}]
VM61519:17 ====parent===== {"id":2,"pId":1}
VM61519:20 ====parent==== {"id":2,"pId":1,"child":[{"id":3,"pId":2}]}
VM61519:21 [{"id":1,"child":[{"id":2,"pId":1,"child":[{"id":3,"pId":2}]}]}]
VM61519:14 ====res=== [{"id":1,"child":[{"id":2,"pId":1,"child":[{"id":3,"pId":2}]}]},{"id":4}]
VM61519:17 ====parent===== {"id":4}
VM61519:20 ====parent==== {"id":4,"child":[{"id":5,"pId":4}]}
VM61519:21 [{"id":1,"child":[{"id":2,"pId":1,"child":[{"id":3,"pId":2}]}]},{"id":4,"child":[{"id":5,"pId":4}]}]

commented

同一个问题? #139

const arr = [
  { id: 1, pid: null },
  { id: 2, pid: 1 },
  { id: 3, pid: 1 },
  { id: 2, pid: 1 },
  { id: 4, pid: 2 },
  { id: 5, pid: 2 },
  { id: 6, pid: 3 },
  { id: 7, pid: 3 },
  { id: 8, pid: 3 },
  { id: 9, pid: 8 },
  { id: 10, pid: 6 }
]

// 去重
const deduplication = (arr) => {
  return arr.filter((item, index) => index === arr.findIndex(child => item.id === child.id))
}

// 递归嵌套
const arrToTree = (arr, pid = null) => {
  return arr.filter(item => item.pid === pid)
   .map(item => ({ ...item, children: arrToTree(arr, item.id)}))
}

const solution = (arr) => {
  return arrToTree(deduplication(arr))
}

solution(arr)

利用对象是引用类型,直接通过pId找到父元素

var data = [{id: 1}, {id: 2, pId: 1},{id: 3, pId: 2}]

function bar() {
  let dataNoRepeat = data.reduce((curr, acc) => {
    curr[acc.id] = acc
    return curr
  }, {})
  let tree = {}

  for(let key in dataNoRepeat) {
    const item = dataNoRepeat[key]
    if(item.pId) {
      let parent = dataNoRepeat[item.pId]
      if(parent) {
        if(!parent.children) {
          parent.children = []
        }
        parent.children.push(item)
      }
    } else {
      tree[item.id] = item
    }
  }

  console.log(tree)
}

bar(data)
  function handelFn(arr) {
    let obj = {}
    let tree = {}
    async function ff(i) {
      obj[arr[i].pId].child.push(arr[i])
    }
    for (let i = 0; i < arr.length; i++) {
      arr[i].child = []
      obj[arr[i].id] = arr[i]
      if (!arr[i].pId) {
        tree [arr [i].id] = arr [i]
      } else {
        ff(i)
      }
    }
    return tree
  }

同问,reduce的复杂度 不用算进去么??

function fn(arr) {
    const res = []
    //创建map结构
    const map = new Map()
    for (let elem of arr.values()) { 
        map[elem.id] = elem
    }
    // 遍历读取
    for (let value of Object.values(map)) {
        if(!value.parentId){
            res.push(value)
        }else{
            const parent = map[value.parentId]
            if(!parent.children) parent.children= []
            parent.children.push(value)
        }
    }
    return res
}

function handelFunc(srcData) {
let childList = [];
let result = [];
for (let i = 0; i < srcData.length; i++) {
let element = srcData[i];
element.child = childList[element.id] || (childList[element.id] = []);
if (!element.pId) {
result.push(element);
} else {
if (childList[element.pId]) {
childList[element.pId].push(element);
} else {
childList[element.pId] = [element];
}
}
}
return result;
}

commented

const find12 = (arr) => {

let map = {}, obj = {}, res = []
//数组去重
arr.forEach(item => {
  if (!obj[JSON.stringify(item)]) {
    obj[JSON.stringify(item)] = item
    map[item.id] = item
  }
})
//转成树形结构
for (const item of Object.values(obj)) {
  if (!item.pid) {
    res.push(item)
  } else {
    if (map[item.pid]) {
      const parent = map[item.pid]
      parent.children = parent.children || []
      parent.children.push(item)
    }
  }
}

return res

}

const map = arr.reduce((res, item) => {
    if (!item.pid) {
        res[item.id] = item
    }
    item.child = arr.filter(it => it.pid && it.pid === item.id)
    return res
}, [])